In this post i will show you a new anti-debug trick that affects many debuggers e.g. WinDbg and IDA Debugger.
When you load a module into the address space of a process usually via calling e.g. the kernel32 "LoadLibrary" function, the debugger is notified of this through the LOAD_DLL_DEBUG_EVENT event. This occurs at the point the "NtMapViewOfSection" function calls the "DbgkMapViewOfSection" function.
As we saw in the previous post, the "HideFromDebugger" flag of the "_ETHREAD" structure and the "DebugPort" field of the "_EPROCESS" structure are queried. If the "HideFromDebugger" flag is not set and the "DebugPort" field is set, the debug event is delivered to the debugger but only after the return value of the "DbgkpSuppressDbgMsg" function is checked.
If the "DbgkpSuppressDbgMsg" function returns false, the debug event is delivered to the debugger and vice versa. Now let's see the "DbgkpSuppressDbgMsg" function in disassembly.
As you can see in the image below, it checks the "SuppressDebugMsg" flag of the 64-bit TEB of the thread. If it is set, the function returns true and the debug event is not delivered to the debugger.
Also, the "SuppressDebugMsg" field of the 32-bit TEB is queried, if the "Wow64Process" field of the "_EPROCESS" structure is set.
Notes:
1) Each Wow64 process has two Process Environment Blocks (PEBs), a 64-bit one and a 32-bit one.
2) Each thread in a Wow64 process has two Thread Information Blocks (TEBs), a 64-bit one and a 32-bit one. The 64-bit TEB is of size 2 pages and the 32-bit TEB is of size 1 page. The 32-bit TEB always follows the 64-bit TEB.
3) If the "Wow64Process" field of the "_EPROCESS" structure is set, then it is a Wow64 process (32-bit process running on 64-bit system). This field holds the address of the process's 32-bit PEB.
In WinDbg and IDA debugger, if our process loads a module e.g. walied.dll via calling e.g. the "LoadLibrary" function, the debugger receives the LOAD_DLL_DEBUG_EVENT event and caches the "hFile" field of the "LOAD_DLL_DEBUG_INFO" structure. It uses the "hFile" field to ReadFile info. e.g. debug info. from walied.dll
The problem here is that WinDbg and IDA debugger don't CloseHandle(hFile) until the UNLOAD_DLL_DEBUG_EVENT event for walied.dll is received. So, if we set the "SuppressDebugMsg" bit of TEB and then call FreeLibrary("walied.dll"), then the debugger will not receive the UNLOAD_DLL_DEBUG_EVENT for walied.dll. Any subsequent attempt to acquire an exclusive access to walied.dll via calling the "CreateFile" function will definitely fail which is a very sign of debugger existence.
A demo can be found here and its source code from here.
The trick mentioned above affects WinDbg and IDA debugger. OllyDbg v1.10 is affected but in a slightly different way. OllyDbg v1.10 does not CloseHandle(hFile) even if the corresponding UNLOAD_DLL_DEBUG_EVENT event is received.
N.B. OllyDbg v2.x is not affected since it immediately CloseHandle the "hFile" field of the "LOAD_DLL_DEBUG_INFO" structure once it receives the LOAD_DLL_DEBUG_EVENT event.
Conclusion:
Setting the "SuppressDebugMsg" bit of thread's TEB prevents the attached debugger from receiving UN/LOAD_DLL_DEBUG_EVENT's from this thread.
For debuggers to be immune to this trick, they should use the "hFile" field to read info. and close this handle immediately.
Any comments or ideas are very welcome.
You can follow me on Twitter @waleedassar
When you load a module into the address space of a process usually via calling e.g. the kernel32 "LoadLibrary" function, the debugger is notified of this through the LOAD_DLL_DEBUG_EVENT event. This occurs at the point the "NtMapViewOfSection" function calls the "DbgkMapViewOfSection" function.
As we saw in the previous post, the "HideFromDebugger" flag of the "_ETHREAD" structure and the "DebugPort" field of the "_EPROCESS" structure are queried. If the "HideFromDebugger" flag is not set and the "DebugPort" field is set, the debug event is delivered to the debugger but only after the return value of the "DbgkpSuppressDbgMsg" function is checked.
If the "DbgkpSuppressDbgMsg" function returns false, the debug event is delivered to the debugger and vice versa. Now let's see the "DbgkpSuppressDbgMsg" function in disassembly.
As you can see in the image below, it checks the "SuppressDebugMsg" flag of the 64-bit TEB of the thread. If it is set, the function returns true and the debug event is not delivered to the debugger.
Also, the "SuppressDebugMsg" field of the 32-bit TEB is queried, if the "Wow64Process" field of the "_EPROCESS" structure is set.
Notes:
1) Each Wow64 process has two Process Environment Blocks (PEBs), a 64-bit one and a 32-bit one.
2) Each thread in a Wow64 process has two Thread Information Blocks (TEBs), a 64-bit one and a 32-bit one. The 64-bit TEB is of size 2 pages and the 32-bit TEB is of size 1 page. The 32-bit TEB always follows the 64-bit TEB.
3) If the "Wow64Process" field of the "_EPROCESS" structure is set, then it is a Wow64 process (32-bit process running on 64-bit system). This field holds the address of the process's 32-bit PEB.
In WinDbg and IDA debugger, if our process loads a module e.g. walied.dll via calling e.g. the "LoadLibrary" function, the debugger receives the LOAD_DLL_DEBUG_EVENT event and caches the "hFile" field of the "LOAD_DLL_DEBUG_INFO" structure. It uses the "hFile" field to ReadFile info. e.g. debug info. from walied.dll
The problem here is that WinDbg and IDA debugger don't CloseHandle(hFile) until the UNLOAD_DLL_DEBUG_EVENT event for walied.dll is received. So, if we set the "SuppressDebugMsg" bit of TEB and then call FreeLibrary("walied.dll"), then the debugger will not receive the UNLOAD_DLL_DEBUG_EVENT for walied.dll. Any subsequent attempt to acquire an exclusive access to walied.dll via calling the "CreateFile" function will definitely fail which is a very sign of debugger existence.
A demo can be found here and its source code from here.
The trick mentioned above affects WinDbg and IDA debugger. OllyDbg v1.10 is affected but in a slightly different way. OllyDbg v1.10 does not CloseHandle(hFile) even if the corresponding UNLOAD_DLL_DEBUG_EVENT event is received.
N.B. OllyDbg v2.x is not affected since it immediately CloseHandle the "hFile" field of the "LOAD_DLL_DEBUG_INFO" structure once it receives the LOAD_DLL_DEBUG_EVENT event.
Conclusion:
Setting the "SuppressDebugMsg" bit of thread's TEB prevents the attached debugger from receiving UN/LOAD_DLL_DEBUG_EVENT's from this thread.
For debuggers to be immune to this trick, they should use the "hFile" field to read info. and close this handle immediately.
Any comments or ideas are very welcome.
You can follow me on Twitter @waleedassar