.netvb.nethookapi-hookdeviare

API Hooking, unexpected handle found on TerminateProcess function


PROBLEM

I've hooked the TerminateProcess calls to identify the process which is being terminated, but I'm getting an unexpected handle from TerminateProcess that does not match with the real process handle that I try to intercept.

I'm using Deviare library but I think this issue could be solved just knowing how the TerminateProcess function works and what I need to do to perform a proper handle comparison.

QUESTION:

If I know the handle of a process which I want to identify its termination, how I could identify that handle from hProcess param of TerminateProcess function?

CODE:

Note the part where I try to compare the handles:

If Process.GetProcessesByName("notepad").FirstOrDefault.Handle = hProcessValue Then ...

It will never be the same handles (I also tried with MainWindowHandle and the Process ID ID)

So, the value of hProcess parameter is very unknown for me.

Imports Nektra.Deviare2

Public NotInheritable Class Form1

Public WithEvents SpyMgr As NktSpyMgr
Public Hook As NktHook

ReadOnly libName As String = "kernel32.dll"
ReadOnly funcName As String = "TerminateProcess"
ReadOnly hookFlags As eNktHookFlags = eNktHookFlags.flgOnlyPreCall

' Processes to attach the hook.
ReadOnly processesToAttach As IEnumerable(Of Process) =
    Process.GetProcessesByName("taskmgr")

Private Sub Test() Handles MyBase.Load

    If Me.processesToAttach.Count = 0 Then
        MsgBox("Any process found.")

    Else
        Me.SpyMgr = New NktSpyMgr()
        Me.SpyMgr.Initialize()

        Me.Hook = SpyMgr.CreateHook(String.Format("{0}!{1}", libName, funcName), hookFlags)
        Me.Hook.Hook(sync:=True)

        For Each proc As Process In processesToAttach
            Debug.WriteLine("Attaching to: " & proc.ProcessName)
            Me.Hook.Attach(procOrId:=proc.Id, sync:=True)
        Next proc

    End If

End Sub

<MTAThread>
Private Sub OnTerminateProcess_Called(ByVal hook As NktHook,
                                      ByVal proc As NktProcess,
                                      ByVal callInfo As NktHookCallInfo) Handles SpyMgr.OnFunctionCalled

    ' Function params.
    Dim hProcessParam As NktParam = DirectCast(callInfo.Params(0), NktParam)
    Dim uExitCodeParam As NktParam = DirectCast(callInfo.Params(1), NktParam)

    ' Param values.
    Dim hProcessValue As IntPtr = New IntPtr(CInt(hProcessParam.Value))
    Dim uExitCodeValue As UInteger = CUInt(uExitCodeParam.Value)

    ' Debuf info.
    Trace.WriteLine(String.Format("hProcess : '{0}'", hProcessValue))
    Trace.WriteLine(String.Format("uExitCode: '{0}'", uExitCodeValue))

    ' Handle Comparison
    If Process.GetProcessesByName("notepad").FirstOrDefault.Handle = hProcessValue Then

        ' Skip precall to avoid process termination.
        If callInfo.IsPreCall Then
            callInfo.Result.Value = 1
            callInfo.SkipCall()
        End If

    End If

End Sub

End Class

RESEARCH:

I've been reading the MSDN doc of TerminateProcess.

Note the parts where it says:

The handle must have the PROCESS_TERMINATE access right

I'm not sure whether I'm missed something to compare the handles.

Also I've been reading here but I didn't take any in clear:

Hooking TerminateProcess & Getting Info From The Handle It Supplies

I also designed this Enum if it could be necessary to perform a good handle comparison:

    Public Enum ProcessAccessFlags As UInteger
       All = &H1F0FFF
       Terminate = &H1
       CreateThread = &H2
       VirtualMemoryOperation = &H8
       VirtualMemoryRead = &H10
       VirtualMemoryWrite = &H20
       DuplicateHandle = &H40
       CreateProcess = &H80
       SetQuota = &H100
       SetInformation = &H200
       QueryInformation = &H400
       QueryLimitedInformation = &H1000
       Synchronize = &H100000
   End Enum

Solution

  • Your code is assuming that there is only one handle per process. This is not right. There is one process ID per process. But every time someone requests a handle for a process, for example with OpenProcess(..., ..., procid) he gets a new process handle (which also depends on the desired access).

    So you cannot compare the handles, you should check the module name or the process id.