You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
943 lines
25 KiB
943 lines
25 KiB
/*++
|
|
|
|
Copyright (c) 1995-1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
suspend.c
|
|
|
|
Abstract:
|
|
|
|
This module implements CpuSuspendThread, CpuGetContext and CpuSetContext for
|
|
AMD64.
|
|
|
|
Author:
|
|
|
|
12-Dec-2001 Samer Arafeh (samera)
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#define _WOW64CPUAPI_
|
|
|
|
|
|
#ifdef _X86_
|
|
#include "amd6432.h"
|
|
#else
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <ntos.h>
|
|
#include "wow64.h"
|
|
#include "wow64cpu.h"
|
|
#include "amd64cpu.h"
|
|
#endif
|
|
|
|
#include "cpup.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
|
|
|
|
ASSERTNAME;
|
|
|
|
|
|
ULONG_PTR ia32ShowContext = 0;
|
|
|
|
VOID
|
|
CpupDebugPrint(
|
|
IN ULONG_PTR Flags,
|
|
IN PCHAR Format,
|
|
...)
|
|
{
|
|
va_list ArgList;
|
|
int BytesWritten;
|
|
CHAR Buffer[ 512 ];
|
|
|
|
if ((ia32ShowContext & Flags) || (Flags == ERRORLOG))
|
|
{
|
|
va_start(ArgList, Format);
|
|
BytesWritten = _vsnprintf(Buffer,
|
|
sizeof(Buffer) - 1,
|
|
Format,
|
|
ArgList);
|
|
if (BytesWritten > 0)
|
|
{
|
|
DbgPrint(Buffer);
|
|
}
|
|
va_end(ArgList);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
CpupPrintContext(
|
|
IN PCHAR str,
|
|
IN PCPUCONTEXT cpu
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Print out the ia32 context based on the passed in cpu context
|
|
|
|
Arguments:
|
|
|
|
str - String to print out as a header
|
|
cpu - Pointer to the per-thread wow64 ia32 context.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
DbgPrint(str);
|
|
DbgPrint("Context addr(0x%p): EIP=0x%08x\n", &(cpu->Context), cpu->Context.Eip);
|
|
DbgPrint("Context EAX=0x%08x, EBX=0x%08x, ECX=0x%08x, EDX=0x%08x\n",
|
|
cpu->Context.Eax,
|
|
cpu->Context.Ebx,
|
|
cpu->Context.Ecx,
|
|
cpu->Context.Edx);
|
|
|
|
DbgPrint("Context ESP=0x%08x, EBP=0x%08x, ESI=0x%08x, EDI=0x%08x\n",
|
|
cpu->Context.Esp,
|
|
cpu->Context.Ebp,
|
|
cpu->Context.Esi,
|
|
cpu->Context.Edi);
|
|
|
|
try {
|
|
|
|
//
|
|
// The stack may not yet be fully formed, so don't
|
|
// let a missing stack cause the process to abort
|
|
//
|
|
|
|
DbgPrint("Context stack=0x%08x 0x%08x 0x%08x 0x%08x\n",
|
|
*((PULONG) cpu->Context.Esp),
|
|
*(((PULONG) cpu->Context.Esp) + 1),
|
|
*(((PULONG) cpu->Context.Esp) + 2),
|
|
*(((PULONG) cpu->Context.Esp) + 3));
|
|
|
|
} except ((GetExceptionCode() == STATUS_ACCESS_VIOLATION)?1:0) {
|
|
|
|
//
|
|
// Got an access violation, so don't print any of the stack
|
|
//
|
|
|
|
DbgPrint("Context stack: Can't get stack contents\n");
|
|
}
|
|
|
|
DbgPrint("Context EFLAGS=0x%08x\n", cpu->Context.EFlags);
|
|
}
|
|
|
|
WOW64CPUAPI
|
|
NTSTATUS
|
|
CpuSuspendThread(
|
|
IN HANDLE ThreadHandle,
|
|
IN HANDLE ProcessHandle,
|
|
IN PTEB Teb,
|
|
OUT PULONG PreviousSuspendCount OPTIONAL)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is entered while the target thread is actually suspended, however, it's
|
|
not known if the target thread is in a consistent state relative to
|
|
the CPU.
|
|
|
|
Arguments:
|
|
|
|
ThreadHandle - Handle of target thread to suspend
|
|
ProcessHandle - Handle of target thread's process
|
|
Teb - Address of the target thread's TEB
|
|
PreviousSuspendCount - Previous suspend count
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS CpupReadBuffer(
|
|
IN HANDLE ProcessHandle,
|
|
IN PVOID Source,
|
|
OUT PVOID Destination,
|
|
IN ULONG Size)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine setup the arguments for the remoted SuspendThread call.
|
|
|
|
Arguments:
|
|
|
|
ProcessHandle - Target process handle to read data from
|
|
Source - Target base address to read data from
|
|
Destination - Address of buffer to receive data read from the specified address space
|
|
Size - Size of data to read
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
|
|
if (ProcessHandle == NtCurrentProcess ()) {
|
|
|
|
try {
|
|
|
|
RtlCopyMemory (Destination,
|
|
Source,
|
|
Size);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
NtStatus = GetExceptionCode ();
|
|
}
|
|
|
|
} else {
|
|
|
|
NtStatus = NtReadVirtualMemory (ProcessHandle,
|
|
Source,
|
|
Destination,
|
|
Size,
|
|
NULL);
|
|
}
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
CpupWriteBuffer(
|
|
IN HANDLE ProcessHandle,
|
|
IN PVOID Target,
|
|
IN PVOID Source,
|
|
IN ULONG Size)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes data to memory taken into consideration if the write is cross-process
|
|
or not
|
|
|
|
Arguments:
|
|
|
|
ProcessHandle - Target process handle to write data into
|
|
Target - Target base address to write data at
|
|
Source - Address of contents to write in the specified address space
|
|
Size - Size of data to write
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
|
|
if (ProcessHandle == NtCurrentProcess ()) {
|
|
|
|
try {
|
|
|
|
RtlCopyMemory (Target,
|
|
Source,
|
|
Size);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
NtStatus = GetExceptionCode ();
|
|
}
|
|
|
|
} else {
|
|
|
|
NtStatus = NtWriteVirtualMemory (ProcessHandle,
|
|
Target,
|
|
Source,
|
|
Size,
|
|
NULL);
|
|
}
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
GetContextRecord(
|
|
IN PCPUCONTEXT cpu,
|
|
IN PCONTEXT ContextAmd64 OPTIONAL,
|
|
IN OUT PCONTEXT32 Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrevies the context record of the specified CPU. This routine updates
|
|
only the registers that are saved on transition to 64-bit mode, and SHOULD
|
|
be kept in sync with the thunk-transition code.
|
|
|
|
Arguments:
|
|
|
|
cpu - CPU to retreive the context record for.
|
|
ContextAmd64 - Full native context.
|
|
Context - IN/OUT pointer to CONTEXT32 to fill in. Context->ContextFlags
|
|
should be used to determine how much of the context to copy.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
ULONG ContextFlags;
|
|
|
|
|
|
try {
|
|
|
|
ContextFlags = Context->ContextFlags;
|
|
|
|
//
|
|
// Get the initial 32-bit context.
|
|
//
|
|
|
|
//
|
|
// Caller will fillup Context with the native context and pass ContextAmd64 as NULL.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT (ContextAmd64)) {
|
|
|
|
Wow64CtxFromAmd64 (ContextFlags,
|
|
ContextAmd64,
|
|
Context);
|
|
|
|
//
|
|
// If the thread is running 32-bit code at the time of retreiving the context,
|
|
// the direct conversion is enough.
|
|
//
|
|
|
|
if (ContextAmd64->SegCs == (KGDT64_R3_CMCODE | RPL_MASK)) {
|
|
return NtStatus;
|
|
}
|
|
}
|
|
|
|
if (ContextFlags & CONTEXT_AMD64) {
|
|
|
|
LOGPRINT((ERRORLOG, "CpuGetContext: Request for amd64 context (0x%x) being FAILED\n", ContextFlags));
|
|
ASSERT((ContextFlags & CONTEXT_AMD64) == 0);
|
|
}
|
|
|
|
if ((ContextFlags & CONTEXT32_CONTROL) == CONTEXT32_CONTROL) {
|
|
|
|
//
|
|
// control registers
|
|
//
|
|
|
|
Context->Ebp = cpu->Context.Ebp;
|
|
Context->Eip = cpu->Context.Eip;
|
|
Context->SegCs = (KGDT64_R3_CMCODE | RPL_MASK);
|
|
Context->EFlags = SANITIZE_X86EFLAGS (cpu->Context.EFlags);
|
|
Context->Esp = cpu->Context.Esp;
|
|
Context->SegSs = (KGDT64_R3_DATA | RPL_MASK);
|
|
}
|
|
|
|
if ((ContextFlags & CONTEXT32_INTEGER) == CONTEXT32_INTEGER) {
|
|
|
|
//
|
|
// i386 integer registers are:
|
|
// edi, esi, ebx, edx, ecx, eax
|
|
//
|
|
|
|
Context->Edi = cpu->Context.Edi;
|
|
Context->Esi = cpu->Context.Esi;
|
|
Context->Ebx = cpu->Context.Ebx;
|
|
Context->Edx = cpu->Context.Edx;
|
|
Context->Ecx = cpu->Context.Ecx;
|
|
Context->Eax = cpu->Context.Eax;
|
|
}
|
|
|
|
if ((ContextFlags & CONTEXT32_SEGMENTS) == CONTEXT32_SEGMENTS) {
|
|
|
|
//
|
|
// i386 segment registers are:
|
|
// ds, es, fs, gs
|
|
// And since they are a constant, force them to be the right values
|
|
//
|
|
|
|
Context->SegDs = (KGDT64_R3_DATA | RPL_MASK);
|
|
Context->SegEs = (KGDT64_R3_DATA | RPL_MASK);
|
|
Context->SegFs = (KGDT64_R3_CMTEB | RPL_MASK);
|
|
Context->SegGs = (KGDT64_R3_DATA | RPL_MASK);
|
|
}
|
|
|
|
if ((ContextFlags & CONTEXT32_FLOATING_POINT) == CONTEXT32_FLOATING_POINT) {
|
|
|
|
//
|
|
// floating point (legacy) registers (ST0-ST7)
|
|
//
|
|
|
|
//
|
|
// This must have already been done.
|
|
//
|
|
}
|
|
|
|
if ((ContextFlags & CONTEXT32_DEBUG_REGISTERS) == CONTEXT32_DEBUG_REGISTERS) {
|
|
|
|
//
|
|
// Debug registers (Dr0-Dr7)
|
|
//
|
|
|
|
//
|
|
// This must have already been done
|
|
//
|
|
}
|
|
|
|
if ((ContextFlags & CONTEXT32_EXTENDED_REGISTERS) == CONTEXT32_EXTENDED_REGISTERS) {
|
|
|
|
//
|
|
// extended floating point registers (XMM0-XMM7)
|
|
//
|
|
}
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
NtStatus = GetExceptionCode ();
|
|
}
|
|
|
|
if (ia32ShowContext & LOG_CONTEXT_GETSET) {
|
|
|
|
CpupPrintContext ("Getting ia32 context: ", cpu);
|
|
}
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
CpupGetContext(
|
|
IN PCONTEXT ContextAmd64,
|
|
IN OUT PCONTEXT32 Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine extracts the context record for the currently executing thread.
|
|
|
|
Arguments:
|
|
|
|
ContextAmd64 - Full native context
|
|
Context - Context record to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
DECLARE_CPU;
|
|
|
|
return GetContextRecord (cpu, ContextAmd64, Context);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CpupGetContextThread(
|
|
IN HANDLE ThreadHandle,
|
|
IN HANDLE ProcessHandle,
|
|
IN PTEB Teb,
|
|
IN OUT PCONTEXT32 Context)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine extract the context record of any thread. This is a generic routine.
|
|
When entered, if the target thread isn't the current thread, then it should be
|
|
guaranteed that the target thread is suspended at a proper CPU state.
|
|
|
|
Arguments:
|
|
|
|
ThreadHandle - Target thread handle to retreive the context for
|
|
ProcessHandle - Open handle to the process that the thread runs in
|
|
Teb - Pointer to the target's thread TEB
|
|
Context - Context record to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
CONTEXT ContextEM;
|
|
PCPUCONTEXT CpuRemoteContext;
|
|
CPUCONTEXT CpuContext;
|
|
|
|
//
|
|
// Get the whole native context
|
|
//
|
|
|
|
ContextEM.ContextFlags = (CONTEXT_FULL |
|
|
CONTEXT_DEBUG_REGISTERS |
|
|
CONTEXT_SEGMENTS);
|
|
|
|
NtStatus = NtGetContextThread (ThreadHandle,
|
|
&ContextEM);
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
|
|
LOGPRINT((ERRORLOG, "CpupGetContextThread: NtGetContextThread (%lx) failed - %lx\n",
|
|
ThreadHandle, NtStatus));
|
|
return NtStatus;
|
|
}
|
|
|
|
//
|
|
// If we are running 64-bit code, then get the context off the 64-bit
|
|
// thread stack, which was spilled by the transition code...
|
|
//
|
|
|
|
if (ContextEM.SegCs != (KGDT64_R3_CMCODE | RPL_MASK)) {
|
|
|
|
LOGPRINT((TRACELOG, "Getting context while thread is executing 64-bit instructions\n"));
|
|
|
|
NtStatus = CpupReadBuffer( ProcessHandle,
|
|
((PCHAR)Teb + FIELD_OFFSET(TEB, TlsSlots[WOW64_TLS_CPURESERVED])),
|
|
&CpuRemoteContext,
|
|
sizeof(CpuRemoteContext));
|
|
|
|
if (NT_SUCCESS(NtStatus)) {
|
|
|
|
NtStatus = CpupReadBuffer (ProcessHandle,
|
|
CpuRemoteContext,
|
|
&CpuContext,
|
|
sizeof(CpuContext));
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
|
|
LOGPRINT((ERRORLOG, "CpupGetContextThread: Couldn't read CPU context %lx - %lx\n",
|
|
CpuRemoteContext, NtStatus));
|
|
}
|
|
} else {
|
|
|
|
LOGPRINT((ERRORLOG, "CpupGetContextThread: Couldn't read CPU context address - %lx\n",
|
|
NtStatus));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the actual context context for the caller
|
|
//
|
|
|
|
if (NT_SUCCESS (NtStatus)) {
|
|
|
|
NtStatus = GetContextRecord (&CpuContext, &ContextEM, Context);
|
|
}
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
|
|
|
|
WOW64CPUAPI
|
|
NTSTATUS
|
|
CpuGetContext(
|
|
IN HANDLE ThreadHandle,
|
|
IN HANDLE ProcessHandle,
|
|
IN PTEB Teb,
|
|
OUT PCONTEXT32 Context)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Extracts the cpu context of the specified thread.
|
|
When entered, it is guaranteed that the target thread is suspended at
|
|
a proper CPU state.
|
|
|
|
Arguments:
|
|
|
|
ThreadHandle - Target thread handle to retreive the context for
|
|
ProcessHandle - Open handle to the process that the thread runs in
|
|
Teb - Pointer to the target's thread TEB
|
|
Context - Context record to fill
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
return CpupGetContextThread(ThreadHandle,
|
|
ProcessHandle,
|
|
Teb,
|
|
Context);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SetContextRecord(
|
|
IN OUT PCPUCONTEXT cpu,
|
|
IN OUT PCONTEXT ContextAmd64 OPTIONAL,
|
|
IN PCONTEXT32 Context,
|
|
OUT PBOOLEAN UpdateNativeContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Update the CPU's register set for the specified CPU. This routine updates
|
|
only the registers that are saved on transition to 64-bit mode, and SHOULD
|
|
be kept in sync with the thunk-transition code.
|
|
|
|
Arguments:
|
|
|
|
cpu - CPU to update its registers
|
|
|
|
Context - Pointer to CONTEXT32 to use. Context->ContextFlags
|
|
should be used to determine how much of the context to update.
|
|
|
|
ContextAmd64 - Full native context, and will hold the updated 32-bit context.
|
|
|
|
UpdateNativeContext - Updated on return to indicate if a NtSetContextThread call is required.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
ULONG ContextFlags;
|
|
ULONG NewContextFlags = 0;
|
|
BOOLEAN CmMode;
|
|
|
|
try {
|
|
|
|
*UpdateNativeContext = FALSE;
|
|
|
|
ContextFlags = Context->ContextFlags;
|
|
|
|
if (ContextFlags & CONTEXT_AMD64) {
|
|
|
|
LOGPRINT((ERRORLOG, "CpuSetContext: Request with amd64 context (0x%x) FAILED\n", ContextFlags));
|
|
ASSERT((ContextFlags & CONTEXT_AMD64) == 0);
|
|
}
|
|
|
|
//
|
|
// Caller might pass NativeContext == NULL if they already know what to do.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT (ContextAmd64)) {
|
|
|
|
CmMode = (ContextAmd64->SegCs == (KGDT64_R3_CMCODE | RPL_MASK));
|
|
|
|
Wow64CtxToAmd64 (ContextFlags,
|
|
Context,
|
|
ContextAmd64);
|
|
|
|
//
|
|
// If we are running 32-bit code
|
|
//
|
|
|
|
if (CmMode == TRUE) {
|
|
*UpdateNativeContext = TRUE;
|
|
return NtStatus;
|
|
}
|
|
|
|
NewContextFlags = 0;
|
|
}
|
|
|
|
if ((ContextFlags & CONTEXT32_CONTROL) == CONTEXT32_CONTROL) {
|
|
|
|
//
|
|
// Control registers
|
|
//
|
|
|
|
cpu->Context.Ebp = Context->Ebp;
|
|
cpu->Context.Eip = Context->Eip;
|
|
cpu->Context.SegCs = (KGDT64_R3_CMCODE | RPL_MASK);
|
|
cpu->Context.EFlags = SANITIZE_X86EFLAGS (Context->EFlags);
|
|
cpu->Context.Esp = Context->Esp;
|
|
cpu->Context.SegSs = Context->SegSs;
|
|
}
|
|
|
|
if ((ContextFlags & CONTEXT32_INTEGER) == CONTEXT32_INTEGER) {
|
|
|
|
//
|
|
// Integer registers
|
|
//
|
|
|
|
cpu->Context.Edi = Context->Edi;
|
|
cpu->Context.Esi = Context->Esi;
|
|
cpu->Context.Ebx = Context->Ebx;
|
|
cpu->Context.Edx = Context->Edx;
|
|
cpu->Context.Ecx = Context->Ecx;
|
|
cpu->Context.Eax = Context->Eax;
|
|
}
|
|
|
|
if ((ContextFlags & CONTEXT32_SEGMENTS) == CONTEXT32_SEGMENTS) {
|
|
|
|
//
|
|
// Segment registers
|
|
//
|
|
|
|
//
|
|
// shouldn't be touched
|
|
//
|
|
}
|
|
|
|
if ((ContextFlags & CONTEXT32_FLOATING_POINT) == CONTEXT32_FLOATING_POINT) {
|
|
|
|
//
|
|
// floating point registers
|
|
//
|
|
|
|
NewContextFlags |= CONTEXT_FLOATING_POINT;
|
|
|
|
}
|
|
|
|
if ((ContextFlags & CONTEXT32_DEBUG_REGISTERS) == CONTEXT32_DEBUG_REGISTERS) {
|
|
|
|
//
|
|
// debug registers (Dr0-Dr7)
|
|
//
|
|
|
|
NewContextFlags |= CONTEXT_DEBUG_REGISTERS;
|
|
}
|
|
|
|
if ((ContextFlags & CONTEXT32_EXTENDED_REGISTERS) == CONTEXT32_EXTENDED_REGISTERS) {
|
|
|
|
//
|
|
// extended floating point registers (ST0-ST7)
|
|
//
|
|
|
|
NewContextFlags |= CONTEXT_FLOATING_POINT;
|
|
|
|
//
|
|
// Save the extended registers so that the trap frame may restore them.
|
|
// The system will ALWAYS scrub XMM0-XMM5 on return from system calls.
|
|
//
|
|
|
|
RtlCopyMemory (cpu->Context.ExtendedRegisters,
|
|
Context->ExtendedRegisters,
|
|
sizeof (Context->ExtendedRegisters));
|
|
}
|
|
|
|
//
|
|
// Whatever they passed in before, it's an X86 context now...
|
|
//
|
|
|
|
cpu->Context.ContextFlags = ContextFlags;
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
NtStatus = GetExceptionCode();
|
|
}
|
|
|
|
|
|
if (ia32ShowContext & LOG_CONTEXT_GETSET) {
|
|
|
|
CpupPrintContext("Setting ia32 context: ", cpu);
|
|
}
|
|
|
|
if (NewContextFlags != 0) {
|
|
|
|
if (ARGUMENT_PRESENT (ContextAmd64)) {
|
|
|
|
ContextAmd64->ContextFlags = NewContextFlags;
|
|
*UpdateNativeContext = TRUE;
|
|
}
|
|
}
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CpupSetContextThread(
|
|
IN HANDLE ThreadHandle,
|
|
IN HANDLE ProcessHandle,
|
|
IN PTEB Teb,
|
|
IN OUT PCONTEXT32 Context)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the context record of any thread. This is a generic routine.
|
|
When entered, if the target thread isn't the currently executing thread, then it should be
|
|
guaranteed that the target thread is suspended at a proper CPU state.
|
|
|
|
Arguments:
|
|
|
|
ThreadHandle - Target thread handle to retreive the context for
|
|
ProcessHandle - Open handle to the process that the thread runs in
|
|
Teb - Pointer to the target's thread TEB
|
|
Context - Context record to set
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
CONTEXT ContextEM;
|
|
PCPUCONTEXT CpuRemoteContext;
|
|
CPUCONTEXT CpuContext;
|
|
BOOLEAN CmMode;
|
|
BOOLEAN UpdateNativeContext;
|
|
|
|
|
|
//
|
|
// Get the whole native context
|
|
//
|
|
|
|
ContextEM.ContextFlags = (CONTEXT_FULL |
|
|
CONTEXT_DEBUG_REGISTERS |
|
|
CONTEXT_SEGMENTS);
|
|
|
|
NtStatus = NtGetContextThread (ThreadHandle,
|
|
&ContextEM);
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
|
|
LOGPRINT((ERRORLOG, "CpupGetContextThread: NtGetContextThread (%lx) failed - %lx\n",
|
|
ThreadHandle, NtStatus));
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
|
|
CmMode = (ContextEM.SegCs == (KGDT64_R3_CMCODE | RPL_MASK));
|
|
|
|
//
|
|
// If we are running 64-bit code, make sure to update the cpu context off the
|
|
// 64-bit thread stack...
|
|
//
|
|
|
|
if (CmMode == FALSE) {
|
|
|
|
LOGPRINT((TRACELOG, "Setting context while thread is executing 64-bit instructions\n"));
|
|
|
|
NtStatus = CpupReadBuffer (ProcessHandle,
|
|
((PCHAR)Teb + FIELD_OFFSET(TEB, TlsSlots[WOW64_TLS_CPURESERVED])),
|
|
&CpuRemoteContext,
|
|
sizeof(CpuRemoteContext));
|
|
|
|
if (NT_SUCCESS(NtStatus)) {
|
|
|
|
NtStatus = CpupReadBuffer (ProcessHandle,
|
|
CpuRemoteContext,
|
|
&CpuContext,
|
|
sizeof(CpuContext));
|
|
} else {
|
|
|
|
LOGPRINT((ERRORLOG, "CpupSetContextThread: Couldn't read CPU context address - %lx\n",
|
|
NtStatus));
|
|
}
|
|
}
|
|
|
|
//
|
|
// We are ready to set the context now
|
|
//
|
|
|
|
if (NT_SUCCESS (NtStatus)) {
|
|
|
|
NtStatus = SetContextRecord (&CpuContext,
|
|
&ContextEM,
|
|
Context,
|
|
&UpdateNativeContext);
|
|
|
|
if (NT_SUCCESS (NtStatus)) {
|
|
|
|
if (CmMode == FALSE) {
|
|
|
|
//
|
|
// If the call is coming thru Wow64, then restore the volatile XMMI and integer registers on the next
|
|
// tranisition to compatibility mode
|
|
//
|
|
|
|
if (ThreadHandle == NtCurrentThread ()) {
|
|
if (((Context->ContextFlags & CONTEXT32_EXTENDED_REGISTERS) == CONTEXT32_EXTENDED_REGISTERS) ||
|
|
((Context->ContextFlags & CONTEXT32_INTEGER) == CONTEXT32_INTEGER)) {
|
|
|
|
CpuContext.TrapFrameFlags = TRAP_FRAME_RESTORE_VOLATILE;
|
|
}
|
|
}
|
|
|
|
NtStatus = CpupWriteBuffer(ProcessHandle,
|
|
CpuRemoteContext,
|
|
&CpuContext,
|
|
sizeof(CpuContext));
|
|
|
|
if (!NT_SUCCESS (NtStatus)) {
|
|
|
|
LOGPRINT((ERRORLOG, "CpupSetContextThread: Couldn't write remote context %lx - %lx\n",
|
|
CpuRemoteContext, NtStatus));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the context ultimately. This shouldn't change except the FP and debug
|
|
// state if the thread is executing in 64-bit (long) mode.
|
|
//
|
|
|
|
if ((NT_SUCCESS (NtStatus)) &&
|
|
(UpdateNativeContext == TRUE)) {
|
|
|
|
NtStatus = NtSetContextThread (ThreadHandle, &ContextEM);
|
|
}
|
|
|
|
} else {
|
|
|
|
LOGPRINT((ERRORLOG, "CpupSetContextThread: Couldn't update native context %lx - %lx\n",
|
|
&ContextEM, NtStatus));
|
|
}
|
|
}
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
|
|
WOW64CPUAPI
|
|
NTSTATUS
|
|
CpuSetContext(
|
|
IN HANDLE ThreadHandle,
|
|
IN HANDLE ProcessHandle,
|
|
IN PTEB Teb,
|
|
PCONTEXT32 Context)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the cpu context for the specified thread.
|
|
When entered, if the target thread isn't the currently executing thread, then it is
|
|
guaranteed that the target thread is suspended at a proper CPU state.
|
|
|
|
Arguments:
|
|
|
|
ThreadHandle - Target thread handle to retreive the context for
|
|
ProcessHandle - Open handle to the process that the thread runs in
|
|
Teb - Pointer to the target's thread TEB
|
|
Context - Context record to set
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
return CpupSetContextThread(ThreadHandle,
|
|
ProcessHandle,
|
|
Teb,
|
|
Context);
|
|
}
|