Tuesday, June 26, 2012

CreateRemoteThread vs. RtlCreateUserThread

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 :

1) The  EAX register to zero.
2) The EIP register to the user-defined entry point.


A question arises here!!. what is this useful for?

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.

N.B. This topic has been tested on Windows XP SP3.

You can follow me on Twitter @waleedassar

2 comments:

  1. Is that the only difference you know, what about informing CSRSS, or creating an activation for the thread ?

    ReplyDelete
    Replies
    1. The post is all about a difference that is only related to anti-attaching.

      If you are interested in more differences, then the "RtlCreateUserThread" function does not include a call to the "ZwResumeThread" function.

      Thanks for leaving you comment, really appreciated.

      Delete