Tuesday, June 26, 2012

In this post i will shed the light on a slight difference between the "CreateRemoteThread" and "RtlCreateUserThread" functions. I will also show how this slight difference could affect your code, esp. if you are implementing an anti-attaching trick.

The difference is in the way the CONTEXT structure is initialized for the new thread. Let's first take the "CreateRemoteThread" function in disassembly.

On Windows XP SP3, at address 0x7C810550, We can see a call to the non-exported "_BaseInitializeContext@20" function which as its name implies sets initial values for registers of the CONTEXT structure.

Here, we focus on only two registers, EIP and EAX which are set in the following manner:

1) The EIP register is set to the address of either "_BaseThreadStartThunk@8" or "_BaseProcessStartThunk@8" depending on the fifth parameter (in this case, the fifth parameter is set to TRUE and EIP is set to the address of "_BaseThreadStartThunk@8").

2) The EAX register is set to the user-defined entry point (User-defined here means the value passed to the "CreateRemoteThread" function in the "lpStartAddress" parameter).

Now the very first thing we conclude is that "BaseThreadStartThunk@8" later executes the user-defined entry point.

Now let's take the "RtlCreateUserThread" function in disassembly and see how the CONTEXT structure for the new thread is initialized.

As you can see in the image above, a different function, "RtlInitializeContext", is used for this task. Going into this function, we can see that it is as simple as setting :

If a thread tries to query its own entry point by calling the "ZwQueryInformationThread" function with the "ThreadInformationClass" parameter set to ThreadQuerySetWin32StartAddress, then the initial value of EAX is the value returned in the "ThreadInformation" parameter. In most cases, this is okay since almost all threads are created by the "CreateRemoteThread" function and hence the user-defined entry point is always returned.

But threads created by the "RtlCreateUserThread" function (e.g. threads created by debuggers to attach to running processes) will not be able to query its own entry point using the "ZwQueryInformationThread" function, since the value returned in the "ThreadInformation" parameter will always be zero as the initial value for EAX was zero.

Imagine a TLS callback running in the context of the attaching thread and trying to query the thread's entry point by calling the "ZwQueryInformationThread" function as part of detecting the debugger, the entry point returned will be zero since the initial value of EAX was zero.

A good solution for this problem is using the "NtQuerySystemInformation" function with the "SystemInformationClass" parameter set to SystemProcessesAndThreadsInformation to get information about all current processes and threads, then locating the proper thread and its SYSTEM_THREAD_INFORMATION structure. Once the right structure is found, the thread entry point can easily be seen in the "StartAddress" member.

The code showing how to use the "NtQuerySystemInformation" function to extract threads entry points can be found here.

An example demonstrating how to use the "NtQuerySystemInformation" function as anti-attaching trick can be found here.