Bypass Process Freeze

May 2, 2024

This time, I will dig into undocumented Thread internals, focusing on a single bit present in KTHREAD structure. KTHREAD is a structure Microsoft mentions in many places (such as KeGetCurrentThread() documentation), but does not officially document, which sounds like a challenge.

Fortunately, Microsoft's public symbols contain some information that allows us to know more. It’s enough to issue the 'dt nt!_KTHREAD' command in the WinDbg, to see what’s inside. 'dt' for displaying type, 'nt' for the ntoskrnl.exe module and the name of the structure prepended with underscore. The underscore is an effect of the way structs are defined in C, making _KTHREAD, KTHREAD, and PTHREAD available, but only the first one included in symbols.

The same applies to other structs, such as _EPROCESS, _TOKEN, _KPCR, etc. Anyway, when you finally display the KTHREAD struct, you can spot the MiscFlags union, containing BypassProcessFreeze bit. I can see it at 0x074, but your offset may vary.

I hope you already know that processes can be suspended. As processes are (at least from the execution point of view) sets of threads, you should be aware that suspending a process practically means suspending all its threads. We can call NtSuspendThread() for all threads in the process, or simply invoke the NtSuspendProcess() syscall and suspend them all at once. The latter method seems so much easier (one call instead of enumerating threads and addressing them individually), better, and recommended. It’s exactly why admins use PsSuspend.exe from SysInternals, or simply right-click process and select "Suspend" option in the Process Explorer.

If you connect the dots, you may obtain quite an interesting picture: the most common way of suspending all threads at once misses threads not willing to be suspended. Is it a bug? Not at all, the flag in the KTHREAD appears for a reason.

What can we do about it? Suspend the entire process and then iterate through threads finding the SuspendCount value seems to be complicated. Calling threads individually may lead to race conditions, where new thread starts before we suspend all runing threads.

To make it even more complex, suspending is not an on/off operation, because it works by changing the SuspendCount counter I have mentioned earlier. We can also obtain some information from NtQueryInformationThread but querying for ThreadSuspendCount is undocumented.

I will not give you the ready answer, but I can make your experiments easier with a special tool, creating a process marking one of its threads with the flag described here.

Feel free to take the tool from https://github.com/gtworek/PSBits/tree/master/SuspendProcess and enjoy!

¯\_(ツ)_/¯

[©] Copyright 2024

GT

Bypass Process Freeze

May 2, 2024

This time, I will dig into undocumented Thread internals, focusing on a single bit present in KTHREAD structure. KTHREAD is a structure Microsoft mentions in many places (such as KeGetCurrentThread() documentation), but does not officially document, which sounds like a challenge.

Fortunately, Microsoft's public symbols contain some information that allows us to know more. It’s enough to issue the 'dt nt!_KTHREAD' command in the WinDbg, to see what’s inside. 'dt' for displaying type, 'nt' for the ntoskrnl.exe module and the name of the structure prepended with underscore. The underscore is an effect of the way structs are defined in C, making _KTHREAD, KTHREAD, and PTHREAD available, but only the first one included in symbols.

The same applies to other structs, such as _EPROCESS, _TOKEN, _KPCR, etc. Anyway, when you finally display the KTHREAD struct, you can spot the MiscFlags union, containing BypassProcessFreeze bit. I can see it at 0x074, but your offset may vary.

I hope you already know that processes can be suspended. As processes are (at least from the execution point of view) sets of threads, you should be aware that suspending a process practically means suspending all its threads. We can call NtSuspendThread() for all threads in the process, or simply invoke the NtSuspendProcess() syscall and suspend them all at once. The latter method seems so much easier (one call instead of enumerating threads and addressing them individually), better, and recommended. It’s exactly why admins use PsSuspend.exe from SysInternals, or simply right-click process and select "Suspend" option in the Process Explorer.

If you connect the dots, you may obtain quite an interesting picture: the most common way of suspending all threads at once misses threads not willing to be suspended. Is it a bug? Not at all, the flag in the KTHREAD appears for a reason.

What can we do about it? Suspend the entire process and then iterate through threads finding the SuspendCount value seems to be complicated. Calling threads individually may lead to race conditions, where new thread starts before we suspend all runing threads.

To make it even more complex, suspending is not an on/off operation, because it works by changing the SuspendCount counter I have mentioned earlier. We can also obtain some information from NtQueryInformationThread but querying for ThreadSuspendCount is undocumented.

I will not give you the ready answer, but I can make your experiments easier with a special tool, creating a process marking one of its threads with the flag described here.

Feel free to take the tool from https://github.com/gtworek/PSBits/tree/master/SuspendProcess and enjoy!

¯\_(ツ)_/¯

[©] Copyright 2024

GT