Detecting KDs with a single instruction
Today, while I was playing with WinDbg I noticed something that some people might find quite interesting.
Especially whoever doesn’t like other people debugging his
malicious code, or at least those that are having fun by making this as hard as it gets, they can now add an extra anti-debugging weapon in their arsenal.
There are several well-known techniques for detecting a user-mode debugger in Windows, such as ‘IsDebuggerPresent’, ‘NtGlobalFlags’, various other tricks based on exceptions (INT3, INT1..), specific debugger detection tricks (I love the memory page guard trick against Olly). The list is quite long, and indeed this is not what this post is about. :)
Going down to detecting a kernel-mode debugger from userland, the most well known way to do this is by using the ‘NtQuerySystemInformation’ function along with the ‘SystemKernelDebuggerInformation’ class. A call to this function returns the values of ‘KdDebuggerEnabled’ and ‘KdDebuggerNotPresent’ flags in AL and AH registers respectively.
However, this method as reliable is, it also has a few disadvantages.
1. Requires dynamic linking usually by [LoadLibrary/GetModuleHandle, GetProcAddress], parsing the exports table of ntdll, etc…
2. Can be easily hooked by anti-anti-reversing plugins and other tools, thus making it trivial to bypass and/or monitor even in userland.
3. It is trivial to set a breakpoint (SW/HW) and break whenever this is used in the most common ways.
Creating a custom way to directly call ‘NtQuerySystemInformation’ by using the syscall number with an inlined implementation of ‘KiFastSystemCall’, is quite effective against hooking but it also requires extra work, and it is not portable accross all Windows versions and x86/x64 builds unless the author implements extra checks to handle everything.
Implementing this method made a lot of sense when most people were still running 32-bit Windows.
However, as you are about to see, making the direct calling of native APIs in userand portable accross al latest Windows versions is probably not worth the effort anymore.
Let’s show a few examples of a 32-bit application calling NtQuerySystemInformation under different Windows versions/builds.
a) Win 7 SP1 x86
mov eax, 42
mov edx, 7FFE0300
call dword ptr ds:[edx] ; ntdll.KiFastSystemCall
mov edx, esp
b) Win 8.1 x64
mov eax, 35
call dword ptr fs:[C0]
c) Win 10 x86
mov eax, 9A
mov edx, esp
d) Win 10 x64
mov eax, 36
mov edx, ntdll.77C3B5D0
mov edx,dword ptr fs:
mov edx,dword ptr ds:[edx+464]
jmp far 0033: ntdll.77C3B5EF
Going back to our subject, thus finding a way to detect if a kernel-mode debugger is present from userland, there is a much easier and reliable way to do so which is also portable across all latest versions of Windows as well as with both x86 and x64 architectures.
In fact, the ‘_KUSER_SHARED_DATA’ structure comes really handy to us with regards to this matter since at offset 0x2D4 contains a field named ‘KdDebuggerEnabled’ which is set to 0x03 if a kernel-mode debugger is active or 0x00 if not. Furthemore, since the base address of this structure is static (0x7FFE0000) accross different Windows versions (even < XP), we can easily use this method to achieve what most people would do through the ‘NtQuerySystemInformation’ function.
The following assembly instruction will work in both 32 and 64-bit applications:
CMP BYTE PTR DS:[7FFE02D4], 3
I have tested this method in Win 7, 8.1 and 10 and and besides the fact that it is 100% reliable it also has several advantages.
1. We don’t need to call any functions, thus it cannot be hooked.
2. ‘_KUSER_SHARED_DATA’ is not writable by default, thus cannot be modified from userland.
3. ‘_KUSER_SHARED_DATA.KdDebuggerEnabled’ field is constantly updated (last 2 bits set to ’11’) by the kernel. So we can’t just patch it once through the KMD and forget about it. See also ‘RtlIsAnyDebuggerPresent’ below for a proper way that only checks those bits. ;)
4. Stealthy! Can be mutated and nicely inlined in our application, thus avoiding pattern detections.
5. Does not require static/dynamic linking of a function, thus it is invisible through imports table scans.
6. Portable across different Windows versions and x86/x64 builds.
As a bonus, I have noticed that there is an interesting undocumented function in Windows which is also the mother of this article, called ‘RtlIsAnyDebuggerPresent’. That’s right!
It checks for both user-mode and kernel-mode debuggers through the PEB.BeingDebugged, basically same check as ‘IsDebuggerPresent’, and the ‘_KUSER_SHARED_DATA.KdDebuggerEnabled‘ as mentioned above.
You have probably noticed, that the actual check is basically looking if the last 2 bits are set by performing an ‘and al, 3’ operation. However, in all my tests the KUSER_SHARED_DATA.KdDebuggerEnabled field was always set to 3 when the kernel mode debugger was active. That being said, there is a possibility that this might change in the future.
Personally, I haven’t seen anyone so far mentioning ‘RtlIsAnyDebuggerPresent’ and the ‘_KUSER_SHARED_DATA.KdDebuggerEnabled‘ method to detect a kernel-mode debugger in Windows. This caused me initially a few second thoughts as if I am currently dublicating someone else’s blog post, but after a quick search I didn’t find any articles mentioning this trick.