Monday, January 27, 2020

Malformed PE Header Kernel Denial Of Service

This post is about a bug in the Windows Kernel that i recently discovered and reported to Microsoft. It lies in code responsible for parsing PE executables.







The "nt!MiCreateImageFileMap" function is prone to a vulnerability where the "e_lfanew" field of the "_IMAGE_DOS_HEADER" structure is not properly checked against the "SizeOfImage" field of the "_IMAGE_OPTIONAL_HEADER" structure. The bug is due to the kernel assuming two contradictive things, the first is that the "e_lfanew" field is an offset into the "on-disk" file and the second is that it is an offset into the "in-memory" executable image.


The function reads (I/O Read) the "_IMAGE_NT_HEADERS" structure as long as data is within the file size, not bearing in mind that PE executables support file overlays. So, while the function reads and parses PE headers "successfully!". Any subsequent call to the "nt!RtlImageNtHeader" or "nt!RtlImageNtHeaderEx" function on the PE Header of the executable image's memory will end up reading beyond memory boundaries (In other words, a file offset is being used to reference memory).

For example, when the "MiCreateImageFileMap" function returns, it calls the "nt!MiValidateImageHeader" function, which calls the "nt!MiMapImageInSystemCache" function to a map number of PTEs corresponding to "SizeOfImage" (total size of PE in memory) and then passes this memory to the "nt!SeValidateImageHeader" function which calls into the Code Integrity driver (CI.dll) functions CI!CiValidateImageHeader, CI!CipFixImageType, nt!RtlImageNtHeader. RtlImageNtHeader feteches e_lfanew again and accesses memory.


So, let's have a look at MiCreateImageFileMap

1) It first starts with a call to "FsRtlGetFileSize()"  function to get the file size of the input executable. If it is is above 0xFFFFFFFF bytes, the call fails. Now, we know the input executable must be below 4GB.



2) It then calls the "IoPageRead" function to read one page representing the input executable's DOS header and hopefully the NT file headers, from disk into memory, if it was not already cached in memory. Then, as expected, it checks to see whether the "e_magic" field is set to "MZ" (0x5A4D).






3) It then makes sure "e_lfanew" plus sizeof(nt!_IMAGE_NT_HEADERS64) does not overflow and does not exceed the input file size on disk.


4) If the data at "e_lfanew" does not fit into one memory page, then function proceeds with allocating two pages and reattempts reading again.


5) Then, it calculates the size and address of _IMAGE_NT_HEADERS structure in memory.

6) At this point, the "MiCreateImageFileMap" function proceeds with parsing the in-memory NT headers by calling  "MiVerifyImageHeader".



 7) The call to "MiCreateImageFileMap" will then proceed to building a   _CONTROL_AREA structure, where the section headers are also parsed via calling the "MiParseImageSectionHeaders" function.



Now let's move to another function, "MiValidateImageHeader". Its prototype looks like below.
It uses the control area structure created previously by "MiCreateImageFileMap" to map the executable header and sections into kernel system cache.

It then calls the "SeValidateImageHeader" function to validate the code integrity of the executable. SeValidateImageHeader itself calls into CI.dll, where "RtlImageNtHeaderEx" is finally called. See, Call stack in image below.


Now, let's move to, "RtlImageNtHeaderEx". Its prototype looks like below.




Its first argument is the executable image's base in memory. It simply finds the location of NT headers in memory by using the "e_lfanew" field.






So, if we modify the "e_lfanew" field to be located in the PE overlay i.e. beyond SizeOfImage, we can have a valid memory image where, at offset 0x3C, we have a value that represents an offset beyond the boundary of image memory in kernel system cache. Moreover, if we set the "IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY" characteristic, we force the "RtlImageNtHeaderEx" to use this file offset as a memory offset, crashing the kernel.










Here is the crash call stack.


By the way, "MiCreateImageFileMap" improperly sanitizing "e_lfanew" against "SizeOfImage" also led to a memory corruption bug in the "nt!MiRelocateImage" function. I will explain that in the next blog post.

You can find POC here. Password: POCpoc@123

The bug was assigned CVE-2019-1391.

You can follow me @waleedassar

Saturday, April 4, 2015

VirtualBox Detection Via WQL Queries

Here i have tried to group most of the WMI classes that can be used to detect VirtualBox Virtual Machine. They are as follows:

1) Win32_NetworkAdapterConfiguration (Alias: NICCONFIG)
2) Win32_SystemDriver (Alias: sysdriver)
3) Win32_NTEventLog (Alias: NTEventLog)
4) Win32_BIOS (Alias: bios)
5) Win32_DiskDrive (Alias: diskdrive)
6) Win32_StartupCommand (Alias: Startup)
7) Win32_ComputerSystem (Alias: ComputerSystem)
8) Win32_Service (Alias: service)
9) Win32_LogicalDisk (Alias: LogicalDisk)
10) Win32_LocalProgramGroup)
11) Win32_NetworkAdapter (Alias: NIC)
12) Win32_Process (Alias: process)
13) Win32_BaseBoard (Alias: BaseBoard)
14) Win32_SystemEnclosure (Alias: SystemEnclosure)
15) Win32_CDROMDrive (Alias: cdrom)
16) WIN32_NetworkClient (Alias: netclient)
17) Win32_ComputerSystemProduct (Alias: csproduct)
18) Win32_VideoController
19) Win32_PnPEntity
20) Win32_NetworkConnection (Alias: NetUse)

I wrote some simple VBScript code for these WQL queries. Here you can find it. It is very self-explanatory.

You can find it on GitHub here.

Tuesday, June 24, 2014

ShareCount As Anti-Debugging Trick

In this post i will share with you an Anti-Debugging trick that is very similar to the "PAGE_EXECUTE_WRITECOPY" trick mentioned here, where we had to flag code section as writeable such that any memory write to its page(s) would force OS to change the page protection from PAGE_EXECUTE_WRITECOPY to PAGE_EXECUTE_READWRITE. But in this case we don't have to make any modifications to the code section's page protection. We will just query the process for its current working set info. Among the stuff we receive querying the working set of a process are two fields, "Shared" and "ShareCount".

By default the OS assumes the memory pages of code section (Non-writable sections) should share physical memory across all process instances. This is true till one process instance commits a memory-write to the shared page. At this point the page becomes no longer shared. Thus, querying the working set of the process and inspecting the "Shared" and/or "ShareCount" fields for our Code section pages would reveal the presence  of  debugger, only if the debugger uses INT3 for breakpoints.


To implement the trick, all you have to do is call the "QueryWorkingSet" or "QueryWorkingSetEx" functions.

N.B. You can also use the "ZwQueryVirtualMemory" function with the "MemoryInformationClass" parameter set to MemoryWorkingSetList for more portable code.

Code from here and demo from here. Tested on Windows 7.

For any suggestions, leave me a comment or drop me a mail waliedassar@gmail.com.

You can also follow me on Twitter @waleedassar

Sunday, February 23, 2014

PE TimeDateStamp Viewer

In this this post, i will share with you a tiny tool that i wrote to discover all occurrences of TimeDateStamps in a PE executable. The tool simply traverses the PE header and specifically the following structures/fields:

1) The "TimeDateStamp" field of the "_IMAGE_FILE_HEADER" structure.

This is the most notorious field that is always a target for both malware authors and forensic guys.

N.B. Certain versions of Delphi linkers always emit a fixed TimeDateStamp of 0x2A425E19, Sat Jun 20 01:22:17 1992. In this case you should not rely on this field and continue looking in other fields.

2) The "TimeDateStamp" field of the "_IMAGE_EXPORT_DIRECTORY" structure.

It is usually the same as or very close to the "TimeDateStamp" field of the the "_IMAGE_FILE_HEADER" structure".

N.B. Not all linkers fill this field, but Microsoft Visual Studio linkers do fill it for both DLL's and EXE's.

3) The "TimeDateStamp" field of the "_IMAGE_IMPORT_DESCRIPTOR" structure.

Unlike what the name implies, this field is a bit useless if you are trying to determine when the executable was built. It is -1 if the executable/dll is bound (see #8) and zero if not. So, it is not implemented in my tool.

4) The "TimeDateStamp" field of the "_IMAGE_RESOURCE_DIRECTORY" structure.

Usually Microsoft Visual Studio linkers don't set it (I have tested with linker versions of 6.0, 8.0, 9.0,  and 10.0).

Borland C and Delphi set this field for the main _IMAGE_RESOURCE_DIRECTORY and its subdirectories.

Sometimes spoofers forget to forge this field for subdirectories.

5) The "TimeDateStamp" of the "_IMAGE_DEBUG_DIRECTORY" structures.

Microsoft Visual Studio linkers emitting debug info. in the final PE always set this field. Spoofers may forge the field in the first "_IMAGE_DEBUG_DIRECTORY" structure and forget the following ones.

N.B. Debug info as pointed to by Debug Data Directory is an array of  "_IMAGE_DEBUG_DIRECTORY" structures, each representing debug info of different type e.g. COFF, CodeView, etc.

6) If  "_IMAGE_DEBUG_DIRECTORY" has the "Type" field set to 0x2 (IMAGE_DEBUG_TYPE_CODEVIEW), then by following the "PointerToRawData" field we can find another occurrence of TimeDateStamp ( only if the PDB format is PDB 2.0 i.e when "Signature" field is set to "NB10" )


7) The "TimeDateStamp" field of the "_IMAGE_LOAD_CONFIG_DIRECTORY" structure.

I have not seen it being used before. However, it is  implemented in the tool.

8) The "TimeDateStamp" field of the "_IMAGE_BOUND_IMPORT_DESCRIPTOR" structures.

It is the TimeDateStamp of the DLL that the executable is bound to. We can't use this field to know when the executable was build, but we can use it to determine on which Windows version/Service pack the file was built/bound. It is not implemented in the tool.

The tool has a very simple command line. See below.

You download the tool from here. For any bugs or suggestions, please don't hesitate to leave me a comment or contant me @waleedassar.

GitHub Project here.

Tuesday, February 12, 2013

Kernel Bug #1 ProcessIoPriority

In this post i will show you the second kernel bug that i found in the  Kernel of Windows 7 SP1 (64-bit). This one is in the "nt!NtSetInformationProcess" function.

Description:
With the "ProcessInformationClass" parameter set to ProcessIoPriority 0x21, passing certain signed values e.g.  0xFFFFFFFF or 0x8000F129 in the variable pointed to by the "ProcessInformation" parameter to the ntdll "ZwSetInformationProcess" function can be abused to arbitrarily set certain bit flags of the corresponding "_EPROCESS" structure e.g. DefaultIoPriority: Pos 27, ProcessSelfDelete : Pos 30, or SetTimerResolutionLink: Pos 31.

Bug Type:
This is due to a signedness error in the "nt!NtSetInformationProcess" function.


32-Bit kernel:


64-bit kernel:

 Impact:
1) The signed value leads to bypassing the check for the "SeIncreaseBasePriorityPrivilege" privilege that is required to set the process's IO priority to HIGH.


2) The signed value leads to bypassing the check for disallowed values for the process's IO priority e.g. the bug can be abused to set the process's IO priority to CRITICAL.

3) Setting the "ProcessSelfDelete" flag, which makes the target process non-killable by conventional methods.

4) Setting the "SetTimerResolutionLink" flag, which causes a BSOD (Bug check code of 0x3B)  if we terminate the process due to a null pointer dereference bug.

Poc:

Non-Killable Process

BSOD

Code:
http://pastebin.com/QejGQXib

Status:
Reported to the vendor.

Any comments or ideas are very welcome. You can also follow me on Twitter @waleedassar

Kernel Bug #0 ThreadIoPriority

This post is the first in a series of posts that will discuss several kernel bugs that i find in Windows Kernel. This post is about a bug found in the kernel of Windows 7 SP1 (64-bit).

Description:
With the "ThreadInformationClass" parameter set to ThreadIoPriority 0x16, passing certain signed values e.g.  0xFF3FFF3C or  0xFF3FFFFC in the variable pointed to by the "ThreadInformation" parameter to the ntdll "ZwSetInformationThread" function can be abused to arbitrarily set certain bit flags of the corresponding "_ETHREAD" structure e.g. ThreadIoPriority:3, ThreadPagePriority:3, RundownFail:1, or NeedsWorkingSetAging:1.

Bug Type:
This is due to a signedness error in the "nt!NtSetInformationThread" function.

32-Bit kernel:
64-bit kernel:


Impact:
1) The signed value leads to bypassing the check for the "SeIncreaseBasePriorityPrivilege" privilege that is required to set the thread's IO priority to HIGH.

2) An unprivileged thread can use certain calculated signed values to escalate its IO priority and memory priority to maximum values e.g. Raise IO priority to CRITICAL or Page priority to 7.

3) Also, certain bit flags of the corresponding "_ETHREAD" structure can be set e.g. RundownFail and NeedsWorkingSetAging.


POC:
https://www.dropbox.com/s/x7zzx5r62h0k4uz/PriorityCheckBypass.exe

Code:
http://pastebin.com/TanNzkn9

Status:
Reported to the vendor and rejected for not being a security issue.

Any comments or ideas are very welcome. You can also follow me on Twitter @waleedassar

Monday, January 28, 2013

Wow64-Specific Anti-Debug Trick

In this post i will show you an anti-debug trick that i have recently found. The trick is specific to Wow64 processes. It rely on the fact that 32-bit debuggers e.g. OllyDbg, IDA Pro Debugger, and WinDbg_x86 don't receive debug events for certain exceptions originating from 64-bit code. One example of these exceptions is EXCEPTION_BREAKPOINT 0x80000003.

N.B. In a Wow64 process in Windows 7, its 32-bit code is executing in CS=0x23, while its 64-bit code is executing in CS=0x33.

Let's take for example the ntdll "DbgPrompt" function in Windows 7 64-bit.  I chose DbgPrompt for two reasons:
1) Calls to it end up with executing the INT 0x2D instruction, which raises an EXCEPTION_BREAKPOINT.
2) The 32-bit version of it (in 32-bit version of ntdll.dll) calls the 64-bit version of it (in 64-bit version of ntdll.dll).

N.B. The ntdll "DbgPrompt" function wraps up calls to the non-exported "DebugPrompt" function.

So, now if we call the "DbgPrompt" function from within our 32-bit code, we know that the call will end up with an EXCEPTION_BREAKPOINT raised from 64-bit mode.

The interesting thing here is that if you call the function without a debugger, the exception will be raised and its exception handler will be called. One the other hand, if a debugger is present, no exceptions are raised and the instruction following INT 2D will be executed.

Given the above knowledge, i wrote a simple demo for that Wow64-specific anti-debug trick. You can download the demo from here and its source code from here.







To bypass this trick, you have to use a 64-bit debugger where the exception will be raised and seen by the debugger.

Any comments or ideas are very welcome.

You can follow me on Twitter @waleedassar

Saturday, January 26, 2013

Wow64Log

In this post i will discuss an interesting functionality that i discovered while reversing Wow64.dll and specifically the "wow64!ProcessInit" function. Now let's take the function into assembly and see how it looks like.

The first thing the function does is open a registry key by calling the "ZwOpenKey" function with the "ObjectAttributes" parameter having the "ObjectName" member set to "REGISTRY\MACHINE\SOFTWARE\Microsoft\WOW64". So our first conclusion here is that the function tries to open the registry key "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Wow64" to retrieve specific information that may affect the Wow64 process throughout its lifetime. Usually, the key does not exist, at least on my machine (Windows 7 SP1 64-bit).


Next, if the key was successfully opened, the "Wow64GetWow64ImageOption" function is then called with the first parameter set to the opened key handle, the second parameter pointing at the wide string "Wow64ExecuteFlags", the third parameter set to 0x4 (REG_DWORD), the fourth parameter pointing at the variable that will receive the returned value, and the fifth parameter set to 0x4 (output size).

The "Wow64GetWow64ImageOption" function opens the IFEO registry key and queries the registry value whose name is the string pointed to by the second parameter and on error, it queries the same registry value under the registry key whose handle was given in the first parameter.

The extracted flags are then used to initialize three Wow64 global variables, Wow64!Wow64CommittedStackSize, Wow64!Wow64MaximumStackSize, and Wow64InfoPtr.


Whether the registry key was successfully opened or not and whether the "Wow64ExecuteFlags" value was successfully extracted or not, the "ProcessInit" function then directly jumps to the code you see in the image below.


As you can see it is trying to load a library called "Wow64Log.dll" residing in the "System32" directory by calling the ntdll "LdrLoadDll" function. Usually, this module does not exist.

N.B. Since this code is x64, the library Wow64Log.dll must be 64-bit.

Then comes some code that tries to resolve certain function addresses from Wow64Log.Dll by calling the "LdrGetProcedureAddress" function.

N.B. The kernel32 "GetProcAddress" function is just a wrap up of the ntdll "LdrGetProcedureAddress" function.

The code we see in the image above tries to resolve addresses of the "Wow64LogInitialize", "Wow64LogSystemService", "Wow64LogMessageArgList", and "Wow64LogTerminate"  functions from the Wow64Log.dll.  If any of the functions' addresses could not be resolved, the function fails and Wow64Log.dll is unloaded from the address space by calling the ntdll "LdrUnloadDll" function.

Assuming Wow64Log.dll was found in NtSystemRoot\\System32 and the above mentioned functions were found to be exported from it, we will have the global wow64.dll function pointers, "pfnWow64LogInitialize", "pfnWow64LogSystemService", "pfnWow64LogMessageArgList", and "pfnWow64LogTerminate" holding the address of the "Wow64LogInitialize", "Wow64LogSystemService", "Wow64LogMessageArgList", and "Wow64LogTerminate" functions respectively. The "Wow64LogInitialize" function will then be immediately called.

The "Wow64LogSystemService" function will be called every time the "Wow64!Wow64SystemServiceEx" function is called i.e. called with every system call being issued. This can be used for system call logging.

The "Wow64LogMessageArgList" function is called by the "Wow64!Wow64LogPrint" function to log certain events, more likely errors.

The "Wow64LogTerminate" function is called upon process termination by the "Wow64!whNtTerminateProcess" function.


The above mentioned topic can be used as a simple method for injecting 64-bit Dll's into Wow64 (32-Bit) processes by dropping Wow64Log.dll into system32.

Here is a simple Wow64Log.dll that i wrote as a demo.


You can follow me on Twitter @waleedassar

Thursday, January 17, 2013

A Real Random VirtualAlloc

In this post i will discuss one disadvantage of using the "VirtualAlloc" function to allocate memory and also suggest a trick to play around this disadvantage.

If you ever used the "VirtualAlloc" function  to allocate memory, you must have noticed that addresses returned are almost the same over instances of the same process. This is due to the "ZwAllocateVirtualMemory" function doing nothing to ensure the randomness of the base address returned, at least in Windows 7.

N.B. VirtualAlloc is just a wrap up of the "VirtualAllocEx" function which is a wrap up of the ntdll "ZwAllocateVirtualMemory" function.

To test that fact, we will create a small application that does almost nothing but calling the "ZwAllocateVirtualMemory" function and printing the base address at which memory has been allocated.
The source code looks like below.

N.B. Even though the ASLR has nothing to do with randomizing the base address of memory returned by ZwAllocateVirtualMemory, we just set the "IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE" bit field for testing purposes.

Compile the above source code and run the application several times. See image below.

As you can see in the image above, the base address is the same (0x30000) across all instances of the process and this poses a security issue.

It seems that Microsoft has taken care of this issue while allocating stack memory for threads. Now in later versions of Windows e.g. Windows 7, the "RtlCreateUserStack" function which is responsible for reserving and committing the memory for threads is calling the "NtSetInformationProcess" function with a new information class to reserve the stack memory at a random address. The new process information class is ProcessThreadStackAllocation 0x29.

Now let's see how this new information class reserves memory.

Looking at the disassembly we can see that the function checks the "StackRandomizationDisabled" flag of the "_EPROCESS" structure. We can also see the function trying to randomize some variable by using the "SystemTime" field of the "SharedUserData" page, and the "RDTSC" instruction.

The function then calls the "MiScanUserAddressSpace" and "ZwAllocateVirtualMemory" functions to reserve memory at a random base address.


Now let's try to test the "ZwSetInformationProcess" function and see if addresses returned are really random. So, we compile the code in the image below and see.
N.B. Setting the "IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE" bit field is necessary for the "StackRandomizationDisabled" bit flag of the "_EPROCESS" structure to be unset.



As you can see in the two images above, in each time we invoked the application we got a random address for the memory allocated.


Conclusion:
using the "ProcessThreadStackAllocation" class of the "ZwSetInformationProcess" function, we can guarantee a random address for memory we allocate which can be considered a security enhancement.

Code and examples for this post can be found here.

You can follow me on Twitter @waleedassar

Monday, January 14, 2013

Call64, Bypassing Wow64 Emulation Layer

In this post i will discuss a piece of code that i wrote to ease the process of issuing 64-bit system calls without passing through the Wow64 emulation layer implemented in Wow64cpu.dll, Wow64.dll, and Wow64win.dll.


I implemented it in a function called "Call64()". Since some arguments in 64-bit system calls are 64 bits long, the "Call64()" function expects its arguments in the form of pointers to LARGE_INTEGER structures. Also, the return value is in the form of a pointer to a LARGE_INTEGER structure.


Let's take the implementation of this function step by step.

The first argument Call64 takes is a pointer to a LARGE_INTEGER structure which will receive the return value (RAX) of this system call. It is the caller's responsibility to allocate this structure. Also, it is the caller's responsibility to type-cast the value returned in it.

The second argument the function takes is the system call number or ordinal e.g. The "ZwWaitForSingleObject" function in Windows 7 has a system call number of 0x1.

This argument is later used to formulate the shellcode used to issue the 64-bit system call.


Since this function is supposed to make 64-bit system calls with different number of arguments, the function is implemented as variadic (A function with an indefinite number of arguments) with the third argument being the number of arguments the system call expects. The next arguments are all in the form of pointers to LARGE_INTEGER structures.

The function prototype is like below:

After we have looked at how the arguments look like, let's see how the function works.

First, given the number of arguments, it calculated the stack space needed and commits it using the "_alloca" function. The newly-allocated stack space is initialized to zero.

The function takes the first four arguments and stores them in RCX, RDX, R8, and R9 respectively. Extra arguments are stored on stack. Also, shadow space is taken care of.

Using the value of the 64-bit mode Code Segment selector, the function makes a Far Call to a 64-bit shellcode responsible for issuing the system call.


Suppose that we want to make a call to the "ZwClose" function using the "Call64" function, what you should do is allocate two LARGE_INTEGER structure, one to hold the value of the "Handle" parameter and the other to receive the return value (RAX). It looks like below.

Other example is the "ProcessConsoleHostProcess" class of the "ZwSetInformationProcess" function. If we trace into this call, we will find that the Wow64 emulation layer implemented in Wow64.dll prevents Wow64 processes from making such call and thus preventing them from changing their console host processes. See implementation of the "wow64!whNtSetInformationProcess" function.

The sole solution to this is to directly make the system call without passing through the Wow64 emulation layer. The call using the "Call64" function is like below.


N.B. You should bear in mind that some system calls expect pointer arguments to be aligned by 8 and this is why we should align them by using e.g. the "_aligned_malloc" function.


Source code and examples can be found here. The function has also been implemented in a Dynamic Link Library, you can find it and its header and .lib files here.

GitHub project from here.

Any comments, ideas, or bug reports are more than welcome.

You can follow me on Twitter @waleedassar

Friday, December 7, 2012

Windows Internals: SkipThreadAttach

In this post i will not present any new tricks but i will instead discuss a new issue introduced in later versions of Windows regarding thread creation.
In a previous post, i quickly explained the ntdll "NtCreateThreadEx" function and its flag HideFromDebugger 0x4 that when passed to the function causes the new thread to be created hidden from debuggers.

In this post we will see another interesting flag that i prefer to call it SuppressDllMains 0x2. Let's see this in disassembly.

As we can see in the image above, the "PspAllocateThread" function inspects the "Flags" parameter. If the SuppressDllMains 0x2 flag is set, then the function sets the "SkipThreadAttach 0x8" bit flag in the new thread's TEB.

Similarly for the 64-bit version of the function. If the "SuppressDllMains" flag is passed, then the "SkipThreadAttach 0x8" bit flag is set in both the 32-bit TEB and 64-bit TEB of the new thread.

N.B. The bit flags are at offset 0xFCA in 32-bit TEB's and at offset 0x17EE in 64-bit TEB's.

Now let's see what the "SkipThreadAttach" bit flag does. To track this, we will have to shift to user-mode.

In OllyDbg, search for the "\xCA\x0F" (0xFCA) in ntdll.dll and see which functions make use of the "SkipThreadAttach 0x8" bit flag.

The ntdll "RtlIsCurrentThreadAttachExempt" function was among the results i found.
This function returns false if the "SkipThreadAttach" bit flag is not set.
If the "SkipThreadAttach" bit flag is set, another bit flag "RanProcessInit 0x20" is tested. If not set, the function returns true. Otherwise, the function returns false. In C code it looks something like below.

Searching for all references to the "RtlIsCurrentThreadAttachExempt" function, i found one interesting place in ntdll.dll where this function is called, that is LdrpInitializeThread. 

The "LdrpInitializeThread" function is for calling the DllMain's of loaded dlls ( and TLS callbacks as well) each time a thread is initializing (with the "fdwReason" parameter set to DLL_THREAD_ATTACH) or is exiting (with the "fdwReason" parameter set to DLL_THREAD_DETACH).

Taking the "LdrpInitializeThread" function in disassembly, we can see that if  the ntdll "RtlIsCurrentThreadAttachExempt" function returns true e.g. due to the "NtCreateThreadEx" function being called with the "Flags" parameter set to SuppressDllMains 0x2, the DllMains and TLS callbacks of loaded modules will not be called in the context of the new thread. See image below.


A good example for this is the "DbgUiIssueRemoteBreakin" function in ntdll.dll of Windows 7. This function is called by the "DebugActiveProcess" function to create the attaching thread in the context of the process to be debugged.
In Windows XP, the thread created by the "DbgUiIssueRemoteBreakin" function caused the DllMains and TLS callbacks of loaded modules to be called, presenting another layer of protection against attaching.
In Windows 7, since the "DbgUiIssueRemoteBreakin" function ends up calling the "NtCreateThreadEx" function with the "Flags" parameter set to 0x2 (SuppressDllMains), no DllMain's or TLS callbacks are called for the debugger thread.

You can download the demo of this post from here and source code from here.

You can follow me on Twitter @waleedassar

Any comments or ideas are very welcome.