Kill Process vs Kill thread using MadCHook

contains all delphi packages mentioned below
DeCoDish
Posts: 17
Joined: Wed Dec 03, 2008 3:10 pm

Kill Process vs Kill thread using MadCHook

Post by DeCoDish »

I wrote a test program using delphi 2007 using MadChook to inject mydll.dll the test program works fine. When I try to end process using taskmanager under XP I get the Access Denied message.

But using Process Explorer from Sysinternals I still can't kill my process but however I can use the Kill Thread method instead to kill my test.exe also I can suspend it! Is there a way to prevent Kill thread and suspend thread!

Am I missing something?? or I need to add more thing? Please help!

Library Code is bellow:

Code: Select all

Library mydll;

Uses
  Windows,
  madRemote,
  madCodeHook,
  madStrings;

Var TerminateProcessNext   : Function (ProcessHandle, ExitCode: DWord) : Bool;  stdcall;
    NtTerminateProcessNext : Function (ProcessHandle, ExitCode: DWord) : DWord; stdcall;

Function ThisIsOurProcess(processHandle: DWord): Boolean;
Var Pid   : DWord;
    ArrCh : Array [0..MAX_PATH] Of Char;
Begin
    Pid:= ProcessHandleToId(ProcessHandle);
    Result:= (Pid <> 0) And ProcessIdToFileName(Pid, ArrCh) And
            (PosText('Test.exe', ArrCh) > 0);
End;

Function TerminateProcessCallback(ProcessHandle, ExitCode: DWord): Bool; stdcall;
Begin
    If ThisIsOurProcess(processHandle) Then
      Begin
          Result:= False;
          SetLastError(ERROR_ACCESS_DENIED);
      End
    Else
      Begin
          Result:= TerminateProcessNext(ProcessHandle, ExitCode);
      End;
End;

Function NtTerminateProcessCallback(ProcessHandle, ExitCode: DWord): DWord; stdcall;
Const
    STATUS_ACCESS_DENIED = $C0000022;
Begin
    If ThisIsOurProcess(processHandle) Then
      Begin
          Result:= STATUS_ACCESS_DENIED;
      End
    Else
      Begin
          Result:= NtTerminateProcessNext(ProcessHandle, ExitCode);
      End;
End;

Begin
    If GetVersion And $80000000 = 0 Then
      HookAPI('ntdll.dll', 'NtTerminateProcess', @NtTerminateProcessCallback, @NtTerminateProcessNext)
    Else
      HookAPI(kernel32, 'TerminateProcess', @TerminateProcessCallback, @TerminateProcessNext)
End.
The Code bellow is what I'm using in my test.exe application to hook
When the application start I call InjectLibrary

InjectLibrary(ALL_SESSIONS or SYSTEM_PROCESSES, 'mydll.dll')

I call the uninjectLibray when I want to quit the application.
UninjectLibrary(ALL_SESSIONS or SYSTEM_PROCESSES, 'mydll.dll')

Help is appreciated
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

You can hook and block the APIs for suspending and terminating threads. These are (Nt)SuspendThread and (Nt)TerminateThread.

I hope this is for strictly legal purpose?
DeCoDish
Posts: 17
Joined: Wed Dec 03, 2008 3:10 pm

Post by DeCoDish »

Thanks Madshi!

I'll try it today at work and see how is works.

and Yes it's for legal purpose for my University.
madshi wrote:You can hook and block the APIs for suspending and terminating threads. These are (Nt)SuspendThread and (Nt)TerminateThread.

I hope this is for strictly legal purpose?
DeCoDish
Posts: 17
Joined: Wed Dec 03, 2008 3:10 pm

Post by DeCoDish »

I did the following and I had 2 times blue screen when I tried to unhook but still I was able to SuspendThread and TerminatThread

I don't know what am I doing wrong but I think my API declaration is wrong... specialy the output...

Please help!

Code: Select all

Library mydll;

Uses
  Windows,
  madRemote,
  madCodeHook,
  madStrings;

Var TerminateProcessNext    : Function (ProcessHandle, ExitCode: Dword) : Bool;  stdcall;
    NtTerminateProcessNext  : Function (ProcessHandle, ExitCode: Dword) : Dword; stdcall;
    SuspendThreadNext       : Function (ProcessHandle: Dword) : Cardinal; stdcall;
    NtSuspendThreadNext     : Function (ProcessHandle: Dword) : Cardinal; stdcall;
    TerminateThreadNext     : Function (ProcessHandle, ExitCode: Dword) : Dword;  stdcall;
    NtTerminateThreadNext   : Function (ProcessHandle, ExitCode: Dword) : Dword;  stdcall;

Function ThisIsOurProcess(ProcessHandle: DWord): Boolean;
Var Pid   : DWord;
    ArrCh : Array [0..MAX_PATH] Of Char;
Begin
    Pid:= ProcessHandleToId(ProcessHandle);
    Result:= (Pid <> 0) And ProcessIdToFileName(Pid, ArrCh) And
            (PosText('Test.exe', ArrCh) > 0);
End;

Function SuspendThreadCallback(ProcessHandle: DWord): LongInt; stdcall;
Begin
    If ThisIsOurProcess(ProcessHandle) Then
      Begin
          Result:= -1;
      End
    Else
      Begin
          Result:= SuspendThreadNext(ProcessHandle);
      End;
End;

Function NtSuspendThreadCallback(ProcessHandle: DWord): LongInt; stdcall;
Begin
    If ThisIsOurProcess(ProcessHandle) Then
      Begin
          Result:= -1;
      End
    Else
      Begin
          Result:= SuspendThreadNext(ProcessHandle);
      End;
End;

Function TerminateThreadCallback(ProcessHandle, ExitCode: DWord): DWord; stdcall;
Begin
    If ThisIsOurProcess(ProcessHandle) Then
      Begin
          Result:= 0;
      End
    Else
      Begin
          Result:= NtTerminateThreadNext(ProcessHandle, ExitCode);
      End;
End;

Function NtTerminateThreadCallback(ProcessHandle, ExitCode: DWord): DWord; stdcall;
Begin
    If ThisIsOurProcess(ProcessHandle) Then
      Begin
          Result:= 0;
      End
    Else
      Begin
          Result:= NtTerminateThreadNext(ProcessHandle, ExitCode);
      End;
End;

Function TerminateProcessCallback(ProcessHandle, ExitCode: DWord): Bool; stdcall;
Begin
    If ThisIsOurProcess(ProcessHandle) Then
      Begin
          Result:= False;
          SetLastError(ERROR_ACCESS_DENIED);
      End
    Else
      Begin
          Result:= TerminateProcessNext(ProcessHandle, ExitCode);
      End;
End;

Function NtTerminateProcessCallback(ProcessHandle, ExitCode: DWord): DWord; stdcall;
Const
    STATUS_ACCESS_DENIED = $C0000022;
Begin
    If ThisIsOurProcess(ProcessHandle) Then
      Begin
          Result:= STATUS_ACCESS_DENIED;
      End
    Else
      Begin
          Result:= NtTerminateProcessNext(ProcessHandle, ExitCode);
      End;
End;

Begin
    If GetVersion And $80000000 = 0 Then
      Begin
          HookAPI('ntdll.dll', 'NtTerminateProcess', @NtTerminateProcessCallback, @NtTerminateProcessNext);
          HookAPI('ntdll.dll', 'NtTerminateThread', @NtTerminateThreadCallback, @NtTerminateThreadNext);
          HookAPI('ntdll.dll', 'NtSuspendThread', @NtSuspendThreadCallback, @NtSuspendThreadNext);
      End
    Else
      Begin
          HookAPI(kernel32, 'TerminateProcess', @TerminateProcessCallback, @TerminateProcessNext);
          HookAPI(kernel32, 'TerminateThread', @TerminateThreadCallback, @TerminateThreadNext);
          HookAPI(kernel32, 'SuspendThread', @SuspendThreadCallback, @SuspendThreadNext);
      End;
End.
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

I'm sorry, but I won't do all the work for you!!

Two tips:

(1) Check out the HookProcessTermination demo.
(2) Look up the API definitions on Google.

It took me 1 minute to find the first bug in your code by doing (2).
DeCoDish
Posts: 17
Joined: Wed Dec 03, 2008 3:10 pm

Post by DeCoDish »

Thanks for your quick reply and I appreciate every second of your time for help.

I've checked your HookProcessTermination and my test.exe as I mentioned it works just fine without the NtSuspendThreadNext and NtTerminateThreadNext. If I exclude those call everything works fine but Process Explorer can always kill it using the suspend and terminate thread.

I did google SuspendThread declaration and I came out with this.

Var NtSuspendThreadNext : Function (ThreadHandle: Dword; PreviousSuspendCount: PULONG) : LongInt; stdcall;

I just wanna make it simple passing parameters without interruption.

Function NtSuspendThreadCallback(ThreadHandle: Dword; PreviousSuspendCount: PULONG) : LongInt; stdcall;
Begin
Result:= NtSuspendThreadNext(ThreadHandle, PreviousSuspendCount);
End;

Begin
If GetVersion And $80000000 = 0 Then
HookAPI('ntdll.dll', 'NtSuspendThread', @NtSuspendThreadCallback, @NtSuspendThreadNext);
End.

Now I just want to know why I'm getting the blue screen of death when I call UninjectLibrary(ALL_SESSIONS or SYSTEM_PROCESSES, 'mydll.dll') for my test.exe application?

Please just point me to the right direction?
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

thread handle <> process handle
DeCoDish
Posts: 17
Joined: Wed Dec 03, 2008 3:10 pm

Post by DeCoDish »

Thanks Madshi for the tips, but I'm facing a bigger problem now which is good coz I've learned new things about Process Handles and Thread Handles and their PIDs...

There must be an easy way to get the parent PID of the current thread because I block the NtSuspendThread I wanna make sure that this is my thread and not other application thread.

Any help will be very appreciated.
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

Look for NtQueryInformationThread. But don't use it for thread handles which are equal to one of those 3:

(1) 0
(2) INVALID_HANDLE_VALUE
(3) GetCurrentThread()
DeCoDish
Posts: 17
Joined: Wed Dec 03, 2008 3:10 pm

Post by DeCoDish »

Ok this is what I've done.

The good news is when I suspend thread using process explorer the thread is not suspended anymore however process Explorer raise an error and needs to be closed. and my thread stays alive!!! but for some reason it doesn't give the Access Denied as it supose to be.

Thank again for all help!

Code: Select all

Function GetProcessHandle(AThreadID: Dword): Dword;
Var
  hThreads: Dword;
  te: THREADENTRY32;
Begin
    Result:= INVALID_HANDLE_VALUE;
    hThreads:= CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    If hThreads = INVALID_HANDLE_VALUE Then Exit;
    Try
      te.dwSize:= sizeof(THREADENTRY32);
      If Not Thread32First(hThreads, te) Then Exit;
      Repeat
        If te.th32ThreadID = AThreadID Then
          Begin
              Result:= OpenProcess(PROCESS_QUERY_INFORMATION Or
                          PROCESS_VM_READ,
                          False,
                          te.th32OwnerProcessID);
              Break;
          End;
      Until Not Thread32Next(hThreads, te);
    Finally
      Closehandle(hThreads);
    End;
End;

Function ThisIsOurProcess(ProcessHandle: DWord): Boolean;
Var Pid   : DWord;
    ArrCh : Array [0..MAX_PATH] Of Char;
Begin
    Pid:= ProcessHandleToId(ProcessHandle);
    Result:= (Pid <> 0) And ProcessIdToFileName(Pid, ArrCh) And
            (PosText('Test.exe', ArrCh) > 0);
End;

Function ThisIsOurThread(ThreadHandle: DWord): Boolean;
Var Pid   : DWord;
    ArrCh : Array [0..MAX_PATH] Of Char;
Begin
    Pid:= ProcessHandleToId(GetProcessHandle(ThreadHandleToId(ThreadHandle)));
    Result:= (Pid <> 0) And ProcessIdToFileName(Pid, ArrCh) And
            (PosText('Test.exe', ArrCh) > 0);
End;

Function NtSuspendThreadCallback(ThreadHandle: Dword; Var PreviousSuspendCount: Dword) : Dword; stdcall;
Const
    STATUS_ACCESS_DENIED = $C0000022;
Begin
    If ThisIsOurThread(ThreadHandle) Then
      Result:= STATUS_ACCESS_DENIED
    Else
      Result:= NtSuspendThreadNext(ThreadHandle, PreviousSuspendCount);
End;
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

Don't know where the crash comes from. However, I don't like the enumeration stuff. Using NtQueryInformationThread would be better. Also your code leaks a process handle.
DeCoDish
Posts: 17
Joined: Wed Dec 03, 2008 3:10 pm

Post by DeCoDish »

Thanks Madshi for the new tip!

Const
ProcessBasicInformation = $00000000;
ThreadBasicInformation = ProcessBasicInformation;
PROCESS_QUERY_INFORMATION = $00000400;
THREAD_QUERY_INFORMATION = PROCESS_QUERY_INFORMATION Shr 4;
NTDLL = 'NTDLL.dll';

Type
PPROCESS_BASIC_INFORMATION = ^PROCESS_BASIC_INFORMATION;
PROCESS_BASIC_INFORMATION = Packed Record
ExitStatus : Dword;
PebBaseAddress : Pointer;
AffinityMask : Dword;
BasePriority : Dword;
UniqueProcessId : Dword;
InheritedUniquePID : Dword;
End;

PClientID = ^TClientID;
TClientID = Packed Record
UniqueProcess : Dword;
UniqueThread : Dword;
End;

PTHREAD_BASIC_INFORMATION = ^THREAD_BASIC_INFORMATION;
THREAD_BASIC_INFORMATION = Packed Record
ExitStatus : Dword;
TebBaseAddress : Pointer;
ClientId : TClientID;
AffinityMask : Dword;
Priority : Dword;
BasePriority : Dword;
End;

TNtQueryInformationProcess = Function(hProcess: THandle;
ProcessInformationClass: Integer;
Var ProcessInformation;
ProcessInformationLength: Integer;
Var ReturnLength: Integer): Integer; stdcall;

TNtQueryInformationThread = Function(hThread: THandle;
ThreadInformationClass: Integer;
Var ThreadInformation;
ThreadInformationLength: Integer;
Var ReturnLength: Integer): Integer; stdcall;

Function PHtoPID(Const hProcess: THandle): Integer; stdcall; //ret -1 = fail
Var
Status: Integer;
pbi: PROCESS_BASIC_INFORMATION;
hDup: THandle;
NtQueryInformationProcess: TNtQueryInformationProcess;
Ret: Integer;
Begin
Result:=-1;
If (Not DuplicateHandle(GetCurrentProcess(),
hProcess,
GetCurrentProcess(),
@hDup,
PROCESS_QUERY_INFORMATION,
False,
0)) Then Exit;
@NtQueryInformationProcess:= GetProcAddress(GetModuleHandleW(NTDLL), 'NtQueryInformationProcess');
If @NtQueryInformationProcess = Nil Then Exit;
ZeroMemory(@pbi, SizeOf(pbi));
Status:= NtQueryInformationProcess(hDup,
ProcessBasicInformation,
pbi,
sizeof(pbi),
Ret);
CloseHandle(hDup);
If (Status >= 0) Then Result:= pbi.UniqueProcessId;
End;

Function THtoTID(Const hThread: THandle): Integer; stdcall; //ret -1 = fail
Var
Status: Integer;
tbi: THREAD_BASIC_INFORMATION;
hDup: THandle;
Ret: Integer;
NtQueryInformationThread: TNtQueryInformationThread;
Begin
Result:= -1;
If (Not DuplicateHandle(GetCurrentProcess(),
hThread,
GetCurrentProcess(),
@hDup,
THREAD_QUERY_INFORMATION,
False,
0)) Then Exit;
@NtQueryInformationThread:= GetProcAddress(GetModuleHandleW(NTDLL), 'NtQueryInformationThread');
If @NtQueryInformationThread = Nil Then Exit;
ZeroMemory(@tbi, SizeOf(tbi));
Status:= NtQueryInformationThread(hDup,
ThreadBasicInformation,
tbi,
SizeOf(tbi),
Ret);
CloseHandle(hDup);
If (Status >= 0) Then Result:= tbi.ClientId.UniqueThread;
End;

but it doesn't help either I'm trying to figure out how to get the parentID of a TID from here... THANKS again!
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

DeCoDish wrote:I'm trying to figure out how to get the parentID of a TID from here...
Result:= tbi.ClientId.UniqueProcess;
DeCoDish
Posts: 17
Joined: Wed Dec 03, 2008 3:10 pm

Post by DeCoDish »

Awesome! SuspendProcess is now blocked it works now finally :)

I wanna do the same think for NtTerminateThread
That what I have done and when I tried to terminate process using Process Explorer my XP machine gave me blue screen of death again.

This is exactly what I have done...


Var
NtTerminateThreadNext : Function (ThreadHandle, ExitCode: Dword) : Dword; stdcall;

Function ThisIsOurThread(ThreadHandle: DWord): Boolean;
Var Pid : DWord;
ArrCh : Array [0..MAX_PATH] Of Char;
Begin
Pid:= THtoPID(ThreadHandle); //This gives the parent ID or a given thread
Result:= (Pid <> 0) And ProcessIdToFileName(Pid, ArrCh) And
(PosText('Test.exe', ArrCh) > 0);
End;

Function NtTerminateThreadCallback(ThreadHandle, ExitCode: DWord): DWord; stdcall;
Const
STATUS_ACCESS_DENIED = $C0000022;
Begin
If ThisIsOurThread(ThreadHandle) Then
Begin
Result:= STATUS_ACCESS_DENIED;
End
Else
Begin
Result:= NtTerminateThreadNext(ThreadHandle, ExitCode);
End;
End;

Begin
If GetVersion And $80000000 = 0 Then
Begin
....
HookAPI('ntdll.dll', 'NtTerminateThread', @NtTerminateThreadCallback, @NtTerminateThreadNext);
End
Else
...
End.
madshi
Site Admin
Posts: 10754
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

You haven't done what I told you to. Reread all my comments.
Post Reply