12 ways to terminate a process

TerminateProcess or NtTerminateProcess

Everyone knows about TerminateProcess. You simply open a handle to the target process and call TerminateProcess. In case TerminateProcess is hooked, you can call the equivalent Native API function NtTerminateProcess.

CreateRemoteThread, ExitProcess

For this method you will have to find the address of ExitProcess within the target process. It is usually the same as ExitProcess for your process, so you can use GetModuleHandle and GetProcAddress. You can then create a thread inside the target process which executes ExitProcess, killing the target process.

NtQuerySystemInformation or toolhelp32, TerminateThread or NtTerminateThread

Simply loop through the threads of the target process and terminate each one using TerminateThread. If it’s hooked, call NtTerminateThread.

NtQuerySystemInformation or toolhelp32, SetThreadContext

Loop through the threads of the target process and set their contexts using SetThreadContext; modify their contexts so that eip points to ExitProcess.


Loop from 0 to 4096 and call DuplicateHandle with Options = 1, leaving TargetProcess and TargetProcessHandle null. This will close most, if not all handles opened by the target process. This method works best for complex applications like security software – it doesn’t crash Notepad, for example.

CreateJobObject, AssignProcessToJobObject, TerminateJobObject (and their Native API equivalents)

Create a job object using CreateJobObject, assign the target process to it using AssignProcessToJobObject, and terminate it using TerminateJobObject. This only works if the process is not already associated with a job object. This technique works well if NtAssignProcessToJobObject and NtTerminateJobObject are not hooked because NtTerminateJobObject calls PsTerminateProcess directly.

NtCreateDebugObject, NtDebugActiveProcess, CloseHandle

People usually implement this technique by using DebugActiveProcess and then exiting the current process. They do this because they don’t know how DebugActiveProcess works. Behind the scenes kernel32 is calling ntdll which calls NtDebugActiveProcess with an already-created debug object. You don’t have to exit the current process for the debuggee to get killed; you just need to close the debug object. When it is closed, the kernel will kill the debuggee using PsTerminateProcess.

To implement this technique, you can create a debug object using NtCreateDebugObject (specifying the kill-on-close flag), debug the process using NtDebugActiveProcess (the process handle needs PROCESS_SUSPEND_RESUME access), and close the handle to the debug object using CloseHandle. Here are the definitions:



NTSTATUS NTAPI NtCreateDebugObject(
    PHANDLE DebugObjectHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    ULONG Flags

NTSTATUS NTAPI NtDebugActiveProcess(
    HANDLE ProcessHandle,
    HANDLE DebugObjectHandle

VirtualQueryEx, VirtualProtectEx

Loop through the memory regions of the target process using VirtualQueryEx and set their protections to PAGE_NOACCESS. The program will crash as soon as it context-switches into user-mode code because it will be unable to read any code from memory.

VirtualQueryEx, WriteProcessMemory

Loop through the memory regions of the target process and write random data to it using WriteProcessMemory.


Call VirtualAllocEx in a loop until you can’t reserve any more memory in the target process. It will crash when it is unable to allocate any more memory.


PsTerminateProcess is an internal kernel-mode function which is not exported by ntoskrnl. You will need to locate it by scanning kernel-mode memory for a specific signature, which I will not post here. WARNING: On XP you should locate PspTerminateProcess instead, which is stdcall. On Vista PsTerminateProcess is thiscall (first argument goes in ecx), so you will need some hand-coded assembly.

typedef NTSTATUS (*_PsTerminateProcess)(
    PEPROCESS Process,
    NTSTATUS ExitStatus


This function is not exported either. WARNING: On XP, there are two arguments. On Vista, there are three.

/* XP */
typedef NTSTATUS (NTAPI *_PspTerminateThreadByPointer51)(
    PETHREAD Thread,
    NTSTATUS ExitStatus

/* Vista */
typedef NTSTATUS (NTAPI *_PspTerminateThreadByPointer60)(
    PETHREAD Thread,
    NTSTATUS ExitStatus,
    BOOLEAN DirectTerminate

A note about process handles

Before you get started on these methods, you will need to know the ways of getting a handle to the victim process. Surely, the only way to do this is by calling OpenProcess/NtOpenProcess, right? Wrong. If you can’t get a handle with the right access because of a hooked function (such as with security software), you can open the target process with SYNCHRONIZE access (or whatever access you think will be granted) and call DuplicateHandle to get new access rights. This won’t always work though because security software vendors are starting to hook ZwDuplicateObject to prevent this.

On Windows Vista there are two Native API functions named NtGetNextProcess and NtGetNextThread. Almost no one knows about this and almost no one hooks these two functions. Here are their definitions:

    HANDLE ProcessHandle,
    ACCESS_MASK DesiredAccess,
    ULONG HandleAttributes,
    ULONG Flags,
    PHANDLE NewProcessHandle

    HANDLE ProcessHandle,
    HANDLE ThreadHandle,
    ACCESS_MASK DesiredAccess,
    ULONG HandleAttributes,
    ULONG Flags,
    PHANDLE NewThreadHandle

5 responses

  1. Nice info wj32 šŸ˜‰


    #13 : Queuing APC to process threads

    1- Loop through the threads of the target process .

    2- For each thread we mark it as SystemThread and set the ApcQueueable flag/field .

    3- We Queue a kernel-mode APC to the thread, ApcRoutine calls PsTerminateSystemThreads to kill the current thread .

    4- Forcibly resume the thread if it’s suspened by setting KTHREAD.SuspendCount & KTHREAD.FreezeCount to zero and signalling the thread suspend semaphore .

    Additional work :
    Implementing our own APC manager to avoid hooks .


  2. Hello

    Don’t know why, that code won’t works.
    The handles are duplicated successfully, but the program still works…

    void KillWithDuplicateHandle (DWORD pid)
    HANDLE hProc = OpenProcess(PROCESS_DUP_HANDLE,FALSE, pid);
    if(hProc != NULL)
    for (ULONG i = 0 ; i <= 4096 ; i++)
    HANDLE hdl;

    if (DuplicateHandle( hProc, (HANDLE)i , NULL, &hdl, 0, FALSE, DUPLICATE_CLOSE_SOURCE))
    printf ("Handle [%d] duplicated and terminatedn", i);
    //else printf ("Unable to kill %dn", pid);
    //else printf ("Unable to kill %dn", pid);

Leave a Reply