Solved

Need to kill a very stubborn Windows Service

Posted on 2006-07-16
9
1,443 Views
Last Modified: 2008-01-09
Hello, I have been searching for code to stop a troublesome service. I found the code below on this site which I modified slightly, however when I try using it I get undeclared identifiers for:

TServiceStatusProcess,
QueryServiceStatusEx,
SC_STATUS_PROCESS_INFO, &
TProcRecordArray

I'm guessing its a missing uses that is causing the error, or perhaps this was from an earlier version of Delphi? Not to sure what the problem is to be honest but any help would be appreciated or even a better method to kill this service. No matter what I have tried thus far I get an access denied error.

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Tlhelp32, WinSvc, DateUtils, WinTypes;


function ServiceGetProcessID( sMachine, sService : string ) : DWord;

var
  //
  // service control
  // manager handle
  schm,
  //
  // service handle
  schs   : SC_Handle;
  //
  //
  // current service status
  buf : TServiceStatusProcess;
  handle : THandle;
  bytesneeded : DWord;
begin
  result := 0;

  // connect to the service
  // control manager
  schm := OpenSCManager(
    PChar(sMachine),
    Nil,
    SC_MANAGER_CONNECT);


  // if successful...
  if(schm > 0)then
  begin
    // open a handle to
    // the specified service
    schs := OpenService(
      schm,
      PChar(sService),
      // we want to
      // query service status
      SERVICE_QUERY_STATUS);

    // if successful...
    if(schs > 0)then
    begin
      // retrieve the current status
      // of the specified service
            bytesneeded := 0 ;
      if(QueryServiceStatusEx(schs, SC_STATUS_PROCESS_INFO, buf, sizeof(TServiceStatusProcess), bytesneeded))then
      begin
       Result := buf.dwProcessId;

      end;

      // close service handle
      CloseServiceHandle(schs);
    end;

    // close service control
    // manager handle
    CloseServiceHandle(schm);
  end;
end;

function SetPrivilege(aPrivilegeName: string; aEnabled: boolean): boolean;
var
  TP : TTokenPrivileges;
  TPPrev : TTokenPrivileges;
  Token : THandle;
  dwRetLen : DWord;
begin
  Result := False;
  OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, Token);
  TP.PrivilegeCount := 1;
  if (LookupPrivilegeValue(nil, PChar(aPrivilegeName), TP.Privileges[0].LUID)) then begin
    if (aEnabled) then
      TP.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED
    else
      TP.Privileges[0].Attributes := 0;
    dwRetLen := 0;
    Result := WinTypes.AdjustTokenPrivileges(Token, False, TP, SizeOf(TPPrev), TPPrev, dwRetLen);
  end;
  CloseHandle(Token);
end;

Function KillProcessByPID( ProcId:Cardinal):Boolean;
var loopint:integer;
   Prochandle:Thandle;
   Killit:Boolean;
   ExitCode:Cardinal;
   ProcRecords:TProcRecordArray ;
   numProcIds : Integer ;
Begin
       if not SetPrivilege('SeDebugPrivilege', True) then exit;
       Prochandle:=OpenProcess(PROCESS_ALL_ACCESS, false,ProcId);
       ExitCode:=0;
       killit:=GetExitCodeProcess(ProcHandle,exitcode);
       TerminateProcess(Prochandle,exitcode) ;
       SetPrivilege('SeDebugPrivilege', False);
end;

I'm using delphi 6 for this project any help anyone could give me on this would be greatly appreciated.
0
Comment
Question by:BigScott_27
  • 5
  • 4
9 Comments
 
LVL 26

Expert Comment

by:Russell Libby
ID: 17118498
You could try the following, which should alleviate the dependacy errors. Example usage first, code follows.

Regards,
Russell

var  dwProcID:      DWORD;
begin

  // Make sure to pass the Service NAME, not the Service Description
  dwProcID:=ServiceGetProcessID(EmptyStr, 'YourServiceName');
  if not(dwProcID = 0) then KillProcessByPID(dwProcID);

end;

-------------------------
unit SvcTerminate;

interface

uses
  Windows, WinSvc;

const
  SC_STATUS_PROCESS_INFO        =  0;

type
  _SERVICE_STATUS_PROCESS       =  packed record
     dwServiceType:             DWORD;
     dwCurrentState:            DWORD;
     dwControlsAccepted:        DWORD;
     dwWin32ExitCode:           DWORD;
     dwServiceSpecificExitCode: DWORD;
     dwCheckPoint:              DWORD;
     dwWaitHint:                DWORD;
     dwProcessId:               DWORD;
     dwServiceFlags:            DWORD;
  end;
  SERVICE_STATUS_PROCESS        =  _SERVICE_STATUS_PROCESS;
  TServiceStatusProcess         =  SERVICE_STATUS_PROCESS;
  LPSERVICE_STATUS_PROCESS      =  ^SERVICE_STATUS_PROCESS;
  PServiceStatusProcess         =  ^TServiceStatusProcess;

function   QueryServiceStatusEx(hService: SC_HANDLE; InfoLevel: Integer; lpBuffer: Pointer; cbBufSize: DWORD; pcbBytesNeeded: PDWORD): BOOL; stdcall; external 'advapi32';

procedure  ModifySecurity(Enable: Boolean);
function   ServiceGetProcessID(MachineName, ServiceName: String): DWORD;
function   KillProcessByPID(ProcessID: DWORD): Boolean;

implementation

function KillProcessByPID(ProcessID: DWORD): Boolean;
var  hProc:         THandle;
begin

  // Set default result
  result:=False;

  // Enable permissions
  ModifySecurity(True);

  // Resource protection
  try
     // Open process
     hProc:=OpenProcess(PROCESS_ALL_ACCESS, False, ProcessID);
     // Check handle
     if not(hProc = 0) then
     begin
        // Resource protection
        try
           // Terminate
           result:=TerminateProcess(hProc, 0);
        finally
           // Close process handle
           CloseHandle(hProc);
        end;
     end;
  finally
     // Disable the permissions
     ModifySecurity(False);
  end;

end;

function ServiceGetProcessID(MachineName, ServiceName: String): DWORD;
var  lpStatus:      TServiceStatusProcess;
     hSCM:          SC_HANDLE;
     hSC:           SC_HANDLE;
     dwNeeded:      DWORD;
begin

  // Default result
  result:=0;

  // Connect to the service control manager
  hSCM:=OpenSCManager(PChar(MachineName), nil, SC_MANAGER_CONNECT);

  // Check handle
  if not(hSCM = 0) then
  begin
     // Resource protection
     try
        // Open service
        hSC:=OpenService(hSCM, PChar(ServiceName), SERVICE_QUERY_STATUS);
        // Check handle
        if not(hSC = 0) then
        begin
           // Resource protection
           try
              // Query service
              if QueryServiceStatusEx(hSC, SC_STATUS_PROCESS_INFO, @lpStatus, SizeOf(TServiceStatusProcess), @dwNeeded) then
              begin
                 // Update the result
                 result:=lpStatus.dwProcessId;
              end;
           finally
              // Close handle
              CloseServiceHandle(hSC);
           end;
        end;
     finally
        // Close handle
        CloseServiceHandle(hSCM);
     end;
  end;

end;

procedure ModifySecurity(Enable: Boolean);
var  hToken:        THandle;
     lpszSecName:   Array [0..2] of PChar;
     tp:            TOKEN_PRIVILEGES;
     tpPrevious:    TOKEN_PRIVILEGES;
     luid:          TLargeInteger;
     cbUnused:      DWORD;
     cbPrevious:    DWORD;
     dwCount:       Integer;
begin

  // Set security names
  lpszSecName[0]:='SeSecurityPrivilege';
  lpszSecName[1]:='SeTcbPrivilege';
  lpszSecName[2]:='SeDebugPrivilege';

      // Enable our process to super-level rights
  if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then
  begin
     // Iterate the security names to elevate
     for dwCount:=Low(lpszSecName) to High(lpszSecName) do
     begin
        cbPrevious:=SizeOf(TOKEN_PRIVILEGES);
        if LookupPrivilegeValue(nil, lpszSecName[dwCount], luid) then
        begin
           tp.PrivilegeCount:=1;
           tp.Privileges[0].Luid:=luid;
           tp.Privileges[0].Attributes:=0;
           if AdjustTokenPrivileges(hToken, False, tp, SizeOf(TOKEN_PRIVILEGES), @tpPrevious, cbPrevious) then
           begin
              tpPrevious.PrivilegeCount:=1;
              tpPrevious.Privileges[0].Luid:=luid;
              if Enable then
                 tpPrevious.Privileges[0].Attributes:=SE_PRIVILEGE_ENABLED
              else
                     tpPrevious.Privileges[0].Attributes:=0;
              if not(AdjustTokenPrivileges(hToken, False, tpPrevious, cbPrevious, nil, cbUnused)) then break;
           end;
        end;
     end;
  end;

end;

end.
0
 

Author Comment

by:BigScott_27
ID: 17121077
   Well I tried your code out last night and it compiles with no errors, it does also in fact kill some services. Unfortunately it does not take out the particular service I would like to be able to terminate. I noticed upon testing that it couldn't take out quite a few services actually.

    I am not positive, but I believe that this services uses a system wide api hook on terminate process. I know this service uses hooks to guard certain files that it creates so I'm guessing in order to protect itself it hooks terminate process and possibly other methods that may be used to terminate it. If anyone know of an alternative method to terminate a service that uses these types of security methods I'm willing to try anything.  
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 17121593

If does not hurt to mention this kind of information beforehand...
Anyways, when debugging the code, can you tell me the following:

- Is the correct process id retured.
- Does the OpenProcess call return the process handle

Russell
0
 

Author Comment

by:BigScott_27
ID: 17122438
Sorry for not mentioning that but I'm learning new things about this particular service as I try different methods of closing it. Like I said this is an assumption that it's hooking terminate process and possibly other close calls, it makes sense though. I have a retail stand alone program called task killer that can take it out no problem, however windows task manager, and all DOS based methods of closing it will always return access denied.

As far as I know the PID returned is correct I can tell you I tried it bypassing finding the PID  and plugging the PID in directly from tasklist, and it didn't shut it down with the correct PID. I believe the process handle is returned from OpenProcess, but it was late last night when I tried your code out so I just tested it fairly quickly and didn't check everything out completely. It did work on some other services I tested it on but I can check it out more thoroughly when I get back home later today.

By the way thanks for your help. ;-)
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 26

Accepted Solution

by:
Russell Libby earned 500 total points
ID: 17122548

If you can get the pid, and can OpenProcess a valid handle, then the following MAY be enough to take it down. I say *may*, because it all depends on what hooks have been set, and at what level. (I do have some expertise in this field). If it has only hooked the kernel calls, then the direct ntdll calls should take it out. If that fails, and you are able to inject a remote thread, then the second handler may work.

Regards,
Russell

---

function   NtTerminateProcess(ProcessHandle: THandle; ExitStatus: Integer): Integer; stdcall; external 'ntdll.dll';
function   QueryServiceStatusEx(hService: SC_HANDLE; InfoLevel: Integer; lpBuffer: Pointer; cbBufSize: DWORD; pcbBytesNeeded: PDWORD): BOOL; stdcall; external 'advapi32';

procedure  ModifySecurity(Enable: Boolean);
function   ServiceGetProcessID(MachineName, ServiceName: String): DWORD;
function   KillProcessByPID(ProcessID: DWORD): Boolean;

implementation

function KillProcessByPID(ProcessID: DWORD): Boolean;
var  hProc:         THandle;
     hThread:       THandle;
     dwThread:      DWORD;
begin

  // Set default result
  result:=False;

  // Enable permissions
  ModifySecurity(True);

  // Resource protection
  try
     // Open process for termination
     hProc:=OpenProcess(PROCESS_TERMINATE, False, ProcessID);
     // Check handle
     if not(hProc = 0) then
     begin
        // Resource protection
        try
           // Terminate
           result:=(NTTerminateProcess(hProc, 0) = ERROR_SUCCESS);
        finally
           // Close process handle
           CloseHandle(hProc);
        end;
     end;
     // Check result
     if (result = False) then
     begin
        // Attempt to open up for thread injection
        hProc:=OpenProcess(PROCESS_ALL_ACCESS, False, ProcessID);
        // Check handle
        if not(hProc = 0) then
        begin
           // Resource protection
           try
              // Create remote thread
              hThread:=CreateRemoteThread(hProc, nil, 0, GetProcAddress(GetModuleHandle('kernel32'), 'ExitProcess'), nil, 0, dwThread);
              // Wait for thread to finish
              if not(hThread = 0) then
              begin
                 // Resource protection
                 try
                    // Wait for the thread to complete
                    WaitForSingleObject(hThread, INFINITE);
                 finally
                    // Close thread handle
                    CloseHandle(hThread);
                 end;
                 // Success
                 result:=True;
              end;
           finally
              // Close process handle
              CloseHandle(hProc);
           end;
        end
     end;
  finally
     // Disable the permissions
     ModifySecurity(False);
  end;

end;
0
 

Author Comment

by:BigScott_27
ID: 17122626
Alright I'll give this a shot when I get home later. I appreciate your help on this matter and will certainly let you know how I make out with it.
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 17125079
Sounds good; just a heads up on what I will be asking for if it does NOT work (to save me from asking for it later):

- Need to know if the ServiceGetProcessID is returning the process ID.
- Does this PID match the process in Task Manager.
- Does the OpenProcess call succeed in getting the handle from the ProcessID.
- If the NTTerminateProcess call fails, what is the error code
- If the CreateRemoteThread call fails, what is the error code.

Thanks,
Russell


0
 

Author Comment

by:BigScott_27
ID: 17127013
Thank you so so much Russell!!! The above code did the trick flawlessly the first time I tried it. I would have been going nuts trying to figure this out on my own.
Thanks again,
Scott
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 17127033
Glad it did the trick and we didn't have to go low-level (asm) on it ;-)

Russell
0

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now