#ifndef CONTEXT_i386 #define CONTEXT_i386 0x00010000 // this assumes that i386 and #define CONTEXT_i486 0x00010000 // i486 have identical context records #endif #define WOW64_CS32 0x23 BOOL GetThreadWow64Context( __in HANDLE Thread, __in_opt HANDLE Process, __inout PWOW64_CONTEXT Ctx32 ) { THREAD_BASIC_INFORMATION ThreadInfo; PWOW64_CONTEXT RemoteCtx; CONTEXT Ctx64; SIZE_T Transferred; ULONG ContextFlags; BOOLEAN CloseProcess; ULONG LastError; CloseProcess = FALSE; Ctx64.ContextFlags = CONTEXT_ALL; // // Determine whether the thread is running in 64-bit mode or not. If it is // running in 64-bit mode then we'll get the saved 32-bit context out of // the pointer in the 64-bit TEB's TLS slot. // // If, however, the thread is running in 32-bit mode, then we'll need to be // converting the 32-bit halves of the 64-bit registers into the // appropriate fields of the WOW64_CONTEXT structure. // if (!GetThreadContext( Thread, &Ctx64)) return FALSE; if (Ctx64.SegCs != WOW64_CS32) { wprintf(L" -- Target is in 64-bit mode, returning saved 32-bit context\n"); // // The thread is running in 64-bit mode. We'll need to find the base // address of the thread's TEB and from there, read the remote TLS // slots and then the actual WOW64_CONTEXT structure residing within // the target. // // Also, if the caller didn't supply a process handle, then we'll // attempt to open one now using the thread's associated process ID in // the THREAD_BASIC_INFORMATION structure. This is the typical // behavior of the Wow64 implementation of GetThreadContext. // if (!NT_SUCCESS(NtQueryInformationThread( Thread, ThreadBasicInformation, &ThreadInfo, sizeof( THREAD_BASIC_INFORMATION ), 0 ))) { SetLastError( ERROR_INVALID_HANDLE ); return FALSE; } // // If we don't have a process handle then we'll need to be opening one // ourselves for the VM read operation. // if (!Process) { Process = OpenProcess( PROCESS_VM_READ, FALSE, HandleToUlong(ThreadInfo.ClientId.UniqueProcess) ); if (!Process) return FALSE; CloseProcess = TRUE; } // // Fetch the second TLS slot. Wow64 assumes (hardcoded) that the // second TLS slot is the WOW64_CONTEXT. This is a bit sloppy, but it // works out as there's not a 64-bit kernel32.dll loaded in a Wow64 // process, so there won't be anyone calling TlsAlloc anyway. // if (!ReadProcessMemory( Process, &((PTEB)ThreadInfo.TebBaseAddress)->TlsSlots[ 1 ], &RemoteCtx, sizeof( PWOW64_CONTEXT ), &Transferred)) { if (CloseProcess) { LastError = GetLastError(); CloseHandle( Process ); SetLastError( LastError ); } return FALSE; } if (Transferred != sizeof( PWOW64_CONTEXT )) { if (CloseProcess) CloseHandle( Process ); SetLastError( ERROR_PARTIAL_COPY ); return FALSE; } // // Now that we've got the PWOW64_CONTEXT pointer, let's read the memory // where the actual context is stored. // // Note that this is unreliable if the remote thread is still running // when we make the call. // // // TODO: Pay attention to the caller's requested context flags and // filter the returned context appropriately. // if (!ReadProcessMemory( Process, (PUCHAR)(RemoteCtx) + sizeof( ULONG ), Ctx32, sizeof( WOW64_CONTEXT ), &Transferred )) { if (CloseProcess) { LastError = GetLastError(); CloseHandle( Process ); SetLastError( LastError ); } return FALSE; } if (Transferred != sizeof( WOW64_CONTEXT )) { if (CloseProcess) CloseHandle( Process ); SetLastError( ERROR_PARTIAL_COPY ); return FALSE; } // // All done. // } else { wprintf(L" -- Target is in 32-bit mode, truncating 64-bit registers\n"); // // The requested thread is executing 32-bit instructions and is not in // the Wow64 layer (e.g. a system call). This means that the 64-bit // thread context is actually reflective of the Wow64 context, except // that we need to convert from an x64 context strucuture to the // WOW64_CONTEXT structure (the high halves of registers are discarded // here). // ContextFlags = Ctx32->ContextFlags | CONTEXT_i386; ZeroMemory( Ctx32, sizeof( WOW64_CONTEXT ) ); Ctx32->ContextFlags = ContextFlags; if (ContextFlags & CONTEXT_CONTROL) { Ctx32->Eip = (ULONG)Ctx64.Rip; Ctx32->EFlags = (ULONG)Ctx64.EFlags; Ctx32->Esp = (ULONG)Ctx64.Rsp; Ctx32->Ebp = (ULONG)Ctx64.Rbp; } if (ContextFlags & CONTEXT_INTEGER) { Ctx32->Eax = (ULONG)Ctx64.Rax; Ctx32->Ebx = (ULONG)Ctx64.Rbx; Ctx32->Ecx = (ULONG)Ctx64.Rcx; Ctx32->Edx = (ULONG)Ctx64.Rdx; Ctx32->Edi = (ULONG)Ctx64.Rdi; Ctx32->Esi = (ULONG)Ctx64.Rsi; } // // TODO: Convert other registers, pay attention to the caller's // requested context flags. For example, floating point registers // would need to be handled here if support for them is desired. // // // All done. // } if (CloseProcess) CloseHandle( Process ); return TRUE; } int __cdecl wmain( int ac, wchar_t **av ) { HANDLE Thread; HANDLE Process; ULONG Tid; ULONG Pid; WOW64_CONTEXT Ctx32; if (ac != 3) { wprintf(L"Usage: wow64ctx \n"); return 0; } Pid = wcstoul( av[ 1 ], 0, 0 ); Tid = wcstoul( av[ 2 ], 0, 0 ); Process = OpenProcess( PROCESS_VM_READ, FALSE, Pid ); if (!Process) { wprintf(L"Error %lu opening process %lu\n", GetLastError(), Pid ); return 0; } Thread = OpenThread( THREAD_QUERY_INFORMATION | THREAD_GET_CONTEXT, FALSE, Tid ); if (!Thread) { wprintf(L"Error %lu opening thread %lu\n", GetLastError(), Tid ); CloseHandle( Thread ); return 0; } Ctx32.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_i386 ; if (GetThreadWow64Context( Thread, Process, &Ctx32)) { wprintf( L"Wow64 context:\n" L"eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\n" L"eip=%08x esp=%08x ebp=%08x\n" L" efl=%08x\n", Ctx32.Eax, Ctx32.Ebx, Ctx32.Ecx, Ctx32.Edx, Ctx32.Esi, Ctx32.Edi, Ctx32.Eip, Ctx32.Esp, Ctx32.Ebp, Ctx32.EFlags ); } else { wprintf(L"Error %lu\n", GetLastError()); } getc( stdin ); CloseHandle( Thread ); CloseHandle( Process ); return 0; }