Beware GetThreadContext on Wow64

If you’re planning on running a 32-bit program of yours under Wow64, one of the things that you may need to watch out for is a subtle change in how GetThreadContext / SetThreadContext operate for Wow64 processes.

Specifically, these operations require additional access rights when operating on a Wow64 context (as is the case of a Wow64 process calling Get/SetThreadContext). This is hinted at in MSDN in the documentation for GetThreadContext, with the following note:

WOW64: The handle must also have THREAD_QUERY_INFORMATION access.

However, the reality of the situation is not completely documented by MDSN.

Under the hood, the Wow64 context (e.g. x86 context) for a thread on Windows x64 is actually stored at a relatively well-known location in the virtual address space of the thread’s process, in user-mode. Specifically, one of the TLS slots in thread’s TLS array is repurposed to point to a block of memory that contains the Wow64 context for the thread (used to read or write the context when the Wow64 “portion” of the thread is not currently executing). This is presumably done for performance reasons, as the Wow64 thunk layer needs to be able to quickly transition to/from x86 mode and x64 mode. By storing the x86 context in user mode, this transition can be managed without a kernel mode call. Instead, a simple far call is used to make the transition from x86 to x64 (and an iretq is used to transition from x64 to x86). For example, the following is what you might see when stepping into a Wow64 layer call with the 64-bit debugger while debugging a 32-bit process:

00000000`7783aebe 64ff15c0000000  call    dword ptr fs:[0C0h]
0:000:x86> t
00000000`759c31b0 ea27369c753300  jmp     0033:759C3627
0:012:x86> t
00000000`759c3627 67448b0424      mov     r8d,dword ptr [esp]

A side effect of the Wow64 register context being stored in user mode, however, is that it is not so easily accessible to a remote process. In order to access the Wow64 context, what needs to occur is that the TEB for a thread in question must be located, then read out of the thread’s process’s address space. From there, the TLS array is processed to locate the pointer to the Wow64 context structure, which is then read (or written) from the thread’s process’s address space.

If you’ve been following along so far, you might see the potential problem here. From the perspective of GetThreadContext, there is no handle to the process associated with the thread in question. In other words, we have a missing link here: In order to retrieve the Wow64 context of the thread whose handle we are given, we need to perform a VM read operation on its process. However, to do that, we need a process handle, but we’ve only got a thread handle.

The way that the Wow64 layer solves this problem is to query the process ID associated with the requested thread, open a new handle to the process with the required access rights, and then performs the necessary VM read / write operations.

Now, normally, this works fine; if you can get a handle to the thread of a process, then you should almost always be able to get a handle to the process itself.

However, the devil is in the details, here. There are situations where you might not have access to open a handle to a process, even though you have a handle to the thread (or even an existing process handle, but you can’t open a new one). These situations are relatively rare, but they do occur from time to time (in fact, we ran into one at work here recently).

The most likely scenario for this issue is when you are dealing with (e.g. creating) a process that is operating under a different security context than your process. For example, if you are creating a process operating as a different user, or are working with a process that has a higher integrity level than the current process, then you might not have access to open a new handle to the process (even if you might already have an existing handle returned by a CreateProcess* family routine.

This turns into a rather frustrating problem, especially if you have a handle to both the process and a thread in the process that you’re modifying; in that case, you already have the handle that the Wow64 layer is trying to open, but you have no way to communicate it to Wow64 (and Wow64 will try and fail to open the handle on its own when you make the GetThreadContext / SetThreadContext call).

There are two effective solutions to this problem, neither of which are particularly pretty.

First, you could reverse engineer the Wow64 layer and figure out the exact specifics behind how it locates the PWOW64_CONTEXT, and implement that logic inline (using your already-existing process handle instead of creating a new one). This has the downside that you’re way into undocumented implementation details land, so there isn’t a guarantee that your code will continue to operate on future Windows versions.

The other option is to temporarily modify the security descriptor of the process to allow you to open a second handle to it for the duration of the GetThreadContext / SetThreadContext calls. Although this works, it’s definitely a pain to have to go muddle around with security descriptors just to get the Wow64 layer to work properly.

Note that if you’re a native x64 process on x64 Windows, and you’re setting the 64-bit context of a 64-bit process, this problem does not apply. (Similarly, if you’re a 32-bit process on 32-bit Windows, things work “as expected” as well.)

So, in case you’ve been getting mysterious STATUS_ACCESS_DENIED errors out of GetThreadContext / SetThreadContext in a Wow64 program, now you know why.

8 Responses to “Beware GetThreadContext on Wow64”

  1. […] I had previously mentioned, this all leads up to a transition to 64-bit mode in user mode in the Wow64 case. From there, the […]

  2. Jimmy says:

    I have some trouble running a custom made debugger from x86 to x64.
    You say “In order to retrieve the Wow64 context of the thread whose handle we are given, we need to perform a VM read operation on its process. However, to do that, we need a process handle, but we’ve only got a thread handle.”

    But how would a handle to the process help me when GetThreadContext() wants a hThread? And what is this VM read you are talking about?

  3. Skywing says:

    The Wow64 context is stored as a PWOW64_CONTEXT pointer in the second TLS slot of the 64-bit TEB of the thread hosting the desired Wow64 thread.

    [from wow64cpu.dll : CpupGetContextThread]

    Here, “r15” is the base address of the 64-bit TEB of the Wow64 thread in question.

    loc_78BC1FFE: ; “Getting context while thread is executi”…
    lea rdx, aGettingContext
    mov ecx, 2
    call CpupDebugPrintOnAmd64
    lea rdx, [r15+1488h]
    mov r9d, 8
    lea r8, [rsp+848h+var_818]
    mov rcx, r14
    call CpupReadBuffer
    mov ebx, eax
    test eax, eax
    js short loc_78BC206B


    mov r9d, 2D4h
    lea r8, [rsp+848h+var_328]
    mov rdx, [rsp+848h+var_818]
    mov rcx, r14
    call CpupReadBuffer

    Note that 0x2d4 is sizeof( WOW64_CONTEXT ) + 8. There is a 4 byte prefix and a 4 byte suffix, in between which is the typical x86 context structure.

    All of this is highly implementation specific, however. You should use Wow64GetThreadContext, which will encapsulate all the implementation details. It will open a handle to the threads process internally and fish out the context structure from the target’s VM.

  4. Jimmy says:

    But Wow64GetThreadContext() only exists in Vista and I’m running XP x64, so I’m basically screwed?

  5. Skywing says:

    Hmm. For some reason I was thinking that it did in fact exist on Srv03 x64 / XP x64. I’ll see about posting some more about this after the current series is done. I’d go for just reimplementing Wow64GetThreadContext yourself for pre-Vista systems as you’ve got an upper bound for what systems it would need to be compatible for, which I think is a lot more comfortable of a position to be in if you’re delving into undocumented land.

  6. […] Jimmy asked me what the recommended way to retrieve the 32-bit context of a Wow64 application on Windows […]

  7. Martin Mocko says:

    Just ran into this problem (I believe).

    Our process was creating child process (with default rights), and then debugged it. When parent process is run in debugger, everything works. But when parent process doesn’t run in debugger, it fails in call to GetThreadContext.

    I believe that when parent process is run in debugger, it gets THREAD_QUERY_INFO set, and then it’s child process inherits it.

  8. Ian Sant says:

    Hi guys I think I am experiencing the same problem.

    Basically I am calling CreateRemoteThread from a x86 process to another x86 process running on 64bit machine, and I am getting a Access is denied. (This works perfectly on x86 processors)

    For testing purposes I created a process with a NULL SECURITY_DESCRIPTOR and tried to CreateRemoteThread into it from another process but not luck..

    Any ideas what I can do to solve this issue??