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.
651 lines
18 KiB
651 lines
18 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
psctx.c
|
|
|
|
Abstract:
|
|
|
|
This procedure implements Get/Set Context Thread
|
|
|
|
Author:
|
|
|
|
Mark Lucovsky (markl) 25-May-1989
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "psp.h"
|
|
|
|
VOID
|
|
PspQueueApcSpecialApc(
|
|
IN PKAPC Apc,
|
|
IN PKNORMAL_ROUTINE *NormalRoutine,
|
|
IN PVOID *NormalContext,
|
|
IN PVOID *SystemArgument1,
|
|
IN PVOID *SystemArgument2
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, NtGetContextThread)
|
|
#pragma alloc_text(PAGE, NtSetContextThread)
|
|
#pragma alloc_text(PAGE, PsGetContextThread)
|
|
#pragma alloc_text(PAGE, PsSetContextThread)
|
|
#pragma alloc_text(PAGE, NtQueueApcThread)
|
|
#pragma alloc_text(PAGE, PspQueueApcSpecialApc)
|
|
#endif
|
|
|
|
VOID
|
|
PspQueueApcSpecialApc(
|
|
IN PKAPC Apc,
|
|
IN PKNORMAL_ROUTINE *NormalRoutine,
|
|
IN PVOID *NormalContext,
|
|
IN PVOID *SystemArgument1,
|
|
IN PVOID *SystemArgument2
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
UNREFERENCED_PARAMETER (NormalRoutine);
|
|
UNREFERENCED_PARAMETER (NormalContext);
|
|
UNREFERENCED_PARAMETER (SystemArgument1);
|
|
UNREFERENCED_PARAMETER (SystemArgument2);
|
|
|
|
ExFreePool(Apc);
|
|
}
|
|
|
|
NTSYSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueueApcThread(
|
|
IN HANDLE ThreadHandle,
|
|
IN PPS_APC_ROUTINE ApcRoutine,
|
|
IN PVOID ApcArgument1,
|
|
IN PVOID ApcArgument2,
|
|
IN PVOID ApcArgument3
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is used to queue a user-mode APC to the specified thread. The APC
|
|
will fire when the specified thread does an alertable wait
|
|
|
|
Arguments:
|
|
|
|
ThreadHandle - Supplies a handle to a thread object. The caller
|
|
must have THREAD_SET_CONTEXT access to the thread.
|
|
|
|
ApcRoutine - Supplies the address of the APC routine to execute when the
|
|
APC fires.
|
|
|
|
ApcArgument1 - Supplies the first PVOID passed to the APC
|
|
|
|
ApcArgument2 - Supplies the second PVOID passed to the APC
|
|
|
|
ApcArgument3 - Supplies the third PVOID passed to the APC
|
|
|
|
Return Value:
|
|
|
|
Returns an NT Status code indicating success or failure of the API
|
|
|
|
--*/
|
|
|
|
{
|
|
PETHREAD Thread;
|
|
NTSTATUS st;
|
|
KPROCESSOR_MODE Mode;
|
|
PKAPC Apc;
|
|
|
|
PAGED_CODE();
|
|
|
|
Mode = KeGetPreviousMode ();
|
|
|
|
st = ObReferenceObjectByHandle (ThreadHandle,
|
|
THREAD_SET_CONTEXT,
|
|
PsThreadType,
|
|
Mode,
|
|
&Thread,
|
|
NULL);
|
|
if (NT_SUCCESS (st)) {
|
|
st = STATUS_SUCCESS;
|
|
if (IS_SYSTEM_THREAD (Thread)) {
|
|
st = STATUS_INVALID_HANDLE;
|
|
} else {
|
|
Apc = ExAllocatePoolWithQuotaTag (NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
|
|
sizeof(*Apc),
|
|
'pasP');
|
|
|
|
if (Apc == NULL) {
|
|
st = STATUS_NO_MEMORY;
|
|
} else {
|
|
KeInitializeApc (Apc,
|
|
&Thread->Tcb,
|
|
OriginalApcEnvironment,
|
|
PspQueueApcSpecialApc,
|
|
NULL,
|
|
(PKNORMAL_ROUTINE)ApcRoutine,
|
|
UserMode,
|
|
ApcArgument1);
|
|
|
|
if (!KeInsertQueueApc (Apc, ApcArgument2, ApcArgument3, 0)) {
|
|
ExFreePool (Apc);
|
|
st = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
}
|
|
ObDereferenceObject (Thread);
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PsGetContextThread(
|
|
IN PETHREAD Thread,
|
|
IN OUT PCONTEXT ThreadContext,
|
|
IN KPROCESSOR_MODE Mode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the usermode context of the specified thread. This
|
|
function will fail if the specified thread is a system thread. It will
|
|
return the wrong answer if the thread is a non-system thread that does
|
|
not execute in user-mode.
|
|
|
|
Arguments:
|
|
|
|
Thread - Supplies a pointer to the thread object from
|
|
which to retrieve context information.
|
|
|
|
ThreadContext - Supplies the address of a buffer that will receive
|
|
the context of the specified thread.
|
|
|
|
Mode - Mode to use for validation checks.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG ContextFlags=0;
|
|
GETSETCONTEXT ContextFrame = {0};
|
|
ULONG ContextLength=0;
|
|
NTSTATUS Status;
|
|
PETHREAD CurrentThread;
|
|
|
|
PAGED_CODE();
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Get previous mode and reference specified thread.
|
|
//
|
|
|
|
CurrentThread = PsGetCurrentThread ();
|
|
|
|
//
|
|
// Attempt to get the context of the specified thread.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Set the default alignment, capture the context flags,
|
|
// and set the default size of the context record.
|
|
//
|
|
|
|
if (Mode != KernelMode) {
|
|
ProbeForReadSmallStructure (ThreadContext,
|
|
FIELD_OFFSET (CONTEXT, ContextFlags) + sizeof (ThreadContext->ContextFlags),
|
|
CONTEXT_ALIGN);
|
|
}
|
|
|
|
ContextFlags = ThreadContext->ContextFlags;
|
|
|
|
//
|
|
// We don't need to re-probe here so long as the structure is smaller
|
|
// than the guard region
|
|
//
|
|
ContextLength = sizeof(CONTEXT);
|
|
ASSERT (ContextLength < 0x10000);
|
|
|
|
#if defined(_X86_)
|
|
//
|
|
// CONTEXT_EXTENDED_REGISTERS is SET, then we want sizeof(CONTEXT) set above
|
|
// otherwise (not set) we only want the old part of the context record.
|
|
//
|
|
if ((ContextFlags & CONTEXT_EXTENDED_REGISTERS) != CONTEXT_EXTENDED_REGISTERS) {
|
|
ContextLength = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
|
|
}
|
|
#endif
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode ();
|
|
}
|
|
|
|
KeInitializeEvent (&ContextFrame.OperationComplete,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
ContextFrame.Context.ContextFlags = ContextFlags;
|
|
|
|
ContextFrame.Mode = Mode;
|
|
if (Thread == CurrentThread) {
|
|
ContextFrame.Apc.SystemArgument1 = NULL;
|
|
ContextFrame.Apc.SystemArgument2 = Thread;
|
|
KeEnterGuardedRegionThread (&CurrentThread->Tcb);
|
|
PspGetSetContextSpecialApc (&ContextFrame.Apc,
|
|
NULL,
|
|
NULL,
|
|
&ContextFrame.Apc.SystemArgument1,
|
|
&ContextFrame.Apc.SystemArgument2);
|
|
|
|
KeLeaveGuardedRegionThread (&CurrentThread->Tcb);
|
|
|
|
//
|
|
// Move context to specfied context record. If an exception
|
|
// occurs, then return the error.
|
|
//
|
|
|
|
try {
|
|
RtlCopyMemory (ThreadContext,
|
|
&ContextFrame.Context,
|
|
ContextLength);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode ();
|
|
}
|
|
|
|
} else {
|
|
KeInitializeApc (&ContextFrame.Apc,
|
|
&Thread->Tcb,
|
|
OriginalApcEnvironment,
|
|
PspGetSetContextSpecialApc,
|
|
NULL,
|
|
NULL,
|
|
KernelMode,
|
|
NULL);
|
|
|
|
if (!KeInsertQueueApc (&ContextFrame.Apc, NULL, Thread, 2)) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
|
|
} else {
|
|
KeWaitForSingleObject (&ContextFrame.OperationComplete,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
//
|
|
// Move context to specfied context record. If an
|
|
// exception occurs, then silently handle it and
|
|
// return success.
|
|
//
|
|
|
|
try {
|
|
RtlCopyMemory (ThreadContext,
|
|
&ContextFrame.Context,
|
|
ContextLength);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode ();
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NtGetContextThread(
|
|
IN HANDLE ThreadHandle,
|
|
IN OUT PCONTEXT ThreadContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the usermode context of the specified thread. This
|
|
function will fail if the specified thread is a system thread. It will
|
|
return the wrong answer if the thread is a non-system thread that does
|
|
not execute in user-mode.
|
|
|
|
Arguments:
|
|
|
|
ThreadHandle - Supplies an open handle to the thread object from
|
|
which to retrieve context information. The handle
|
|
must allow THREAD_GET_CONTEXT access to the thread.
|
|
|
|
ThreadContext - Supplies the address of a buffer that will receive
|
|
the context of the specified thread.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
KPROCESSOR_MODE Mode;
|
|
NTSTATUS Status;
|
|
PETHREAD Thread;
|
|
PETHREAD CurrentThread;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get previous mode and reference specified thread.
|
|
//
|
|
|
|
CurrentThread = PsGetCurrentThread ();
|
|
Mode = KeGetPreviousModeByThread (&CurrentThread->Tcb);
|
|
|
|
Status = ObReferenceObjectByHandle (ThreadHandle,
|
|
THREAD_GET_CONTEXT,
|
|
PsThreadType,
|
|
Mode,
|
|
&Thread,
|
|
NULL);
|
|
|
|
//
|
|
// If the reference was successful, the check if the specified thread
|
|
// is a system thread.
|
|
//
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
|
|
//
|
|
// If the thread is not a system thread, then attempt to get the
|
|
// context of the thread.
|
|
//
|
|
|
|
if (IS_SYSTEM_THREAD (Thread) == FALSE) {
|
|
|
|
Status = PsGetContextThread (Thread, ThreadContext, Mode);
|
|
|
|
} else {
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
ObDereferenceObject (Thread);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PsSetContextThread(
|
|
IN PETHREAD Thread,
|
|
IN PCONTEXT ThreadContext,
|
|
IN KPROCESSOR_MODE Mode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function sets the usermode context of the specified thread. This
|
|
function will fail if the specified thread is a system thread. It will
|
|
return the wrong answer if the thread is a non-system thread that does
|
|
not execute in user-mode.
|
|
|
|
Arguments:
|
|
|
|
Thread - Supplies the thread object from
|
|
which to retrieve context information.
|
|
|
|
ThreadContext - Supplies the address of a buffer that contains new
|
|
context for the specified thread.
|
|
|
|
Mode - Mode to use for validation checks.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG ContextFlags=0;
|
|
GETSETCONTEXT ContextFrame;
|
|
ULONG ContextLength=0;
|
|
NTSTATUS Status;
|
|
PETHREAD CurrentThread;
|
|
|
|
PAGED_CODE();
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Get previous mode and reference specified thread.
|
|
//
|
|
|
|
CurrentThread = PsGetCurrentThread ();
|
|
|
|
//
|
|
// Attempt to get the context of the specified thread.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Capture the context flags,
|
|
// and set the default size of the context record.
|
|
//
|
|
|
|
if (Mode != KernelMode) {
|
|
ProbeForReadSmallStructure (ThreadContext,
|
|
FIELD_OFFSET (CONTEXT, ContextFlags) + sizeof (ThreadContext->ContextFlags),
|
|
CONTEXT_ALIGN);
|
|
}
|
|
|
|
//
|
|
// We don't need to re-probe here so long as the structure is small
|
|
// enough not to cross the guard region.
|
|
//
|
|
ContextFlags = ThreadContext->ContextFlags;
|
|
ContextLength = sizeof (CONTEXT);
|
|
ASSERT (ContextLength < 0x10000);
|
|
|
|
#if defined(_X86_)
|
|
//
|
|
// CONTEXT_EXTENDED_REGISTERS is SET, then we want sizeof(CONTEXT) set above
|
|
// otherwise (not set) we only want the old part of the context record.
|
|
//
|
|
if ((ContextFlags & CONTEXT_EXTENDED_REGISTERS) != CONTEXT_EXTENDED_REGISTERS) {
|
|
ContextLength = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
|
|
}
|
|
#endif
|
|
|
|
RtlCopyMemory (&ContextFrame.Context, ThreadContext, ContextLength);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
return GetExceptionCode ();
|
|
}
|
|
|
|
//
|
|
// set the context of the target thread.
|
|
//
|
|
|
|
|
|
#if defined (_IA64_)
|
|
|
|
//
|
|
// On IA64 we need to fix up the PC if its a PLABEL address.
|
|
//
|
|
|
|
if ((ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) {
|
|
|
|
PLABEL_DESCRIPTOR Label, *LabelAddress;
|
|
SIZE_T BytesCopied;
|
|
|
|
if (ContextFrame.Context.IntGp == 0) {
|
|
LabelAddress = (PPLABEL_DESCRIPTOR)ContextFrame.Context.StIIP;
|
|
try {
|
|
//
|
|
// We are in the wrong process here but it doesn't matter.
|
|
// We just want to make sure this isn't a kernel address.
|
|
//
|
|
ProbeForReadSmallStructure (LabelAddress,
|
|
sizeof (*LabelAddress),
|
|
sizeof (ULONGLONG));
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode ();
|
|
}
|
|
|
|
Status = MmCopyVirtualMemory (THREAD_TO_PROCESS (Thread),
|
|
LabelAddress,
|
|
PsGetCurrentProcessByThread (CurrentThread),
|
|
&Label,
|
|
sizeof (Label),
|
|
KernelMode, // Needed to write to local stack
|
|
&BytesCopied);
|
|
if (NT_SUCCESS (Status)) {
|
|
ContextFrame.Context.IntGp = Label.GlobalPointer;
|
|
ContextFrame.Context.StIIP = Label.EntryPoint;
|
|
ContextFrame.Context.StIPSR &= ~ISR_EI_MASK;
|
|
} else {
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
KeInitializeEvent (&ContextFrame.OperationComplete,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
ContextFrame.Context.ContextFlags = ContextFlags;
|
|
|
|
ContextFrame.Mode = Mode;
|
|
if (Thread == CurrentThread) {
|
|
ContextFrame.Apc.SystemArgument1 = (PVOID)1;
|
|
ContextFrame.Apc.SystemArgument2 = Thread;
|
|
KeEnterGuardedRegionThread (&CurrentThread->Tcb);
|
|
PspGetSetContextSpecialApc (&ContextFrame.Apc,
|
|
NULL,
|
|
NULL,
|
|
&ContextFrame.Apc.SystemArgument1,
|
|
&ContextFrame.Apc.SystemArgument2);
|
|
|
|
KeLeaveGuardedRegionThread (&CurrentThread->Tcb);
|
|
|
|
} else {
|
|
KeInitializeApc (&ContextFrame.Apc,
|
|
&Thread->Tcb,
|
|
OriginalApcEnvironment,
|
|
PspGetSetContextSpecialApc,
|
|
NULL,
|
|
NULL,
|
|
KernelMode,
|
|
NULL);
|
|
|
|
if (!KeInsertQueueApc (&ContextFrame.Apc, (PVOID)1, Thread, 2)) {
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
|
|
} else {
|
|
KeWaitForSingleObject (&ContextFrame.OperationComplete,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NtSetContextThread(
|
|
IN HANDLE ThreadHandle,
|
|
IN PCONTEXT ThreadContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function sets the usermode context of the specified thread. This
|
|
function will fail if the specified thread is a system thread. It will
|
|
return the wrong answer if the thread is a non-system thread that does
|
|
not execute in user-mode.
|
|
|
|
Arguments:
|
|
|
|
ThreadHandle - Supplies an open handle to the thread object from
|
|
which to retrieve context information. The handle
|
|
must allow THREAD_SET_CONTEXT access to the thread.
|
|
|
|
ThreadContext - Supplies the address of a buffer that contains new
|
|
context for the specified thread.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
KPROCESSOR_MODE Mode;
|
|
NTSTATUS Status;
|
|
PETHREAD Thread;
|
|
PETHREAD CurrentThread;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get previous mode and reference specified thread.
|
|
//
|
|
|
|
CurrentThread = PsGetCurrentThread ();
|
|
Mode = KeGetPreviousModeByThread (&CurrentThread->Tcb);
|
|
|
|
Status = ObReferenceObjectByHandle (ThreadHandle,
|
|
THREAD_SET_CONTEXT,
|
|
PsThreadType,
|
|
Mode,
|
|
&Thread,
|
|
NULL);
|
|
|
|
//
|
|
// If the reference was successful, the check if the specified thread
|
|
// is a system thread.
|
|
//
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
|
|
//
|
|
// If the thread is not a system thread, then attempt to get the
|
|
// context of the thread.
|
|
//
|
|
|
|
if (IS_SYSTEM_THREAD (Thread) == FALSE) {
|
|
|
|
Status = PsSetContextThread (Thread, ThreadContext, Mode);
|
|
|
|
} else {
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
ObDereferenceObject (Thread);
|
|
}
|
|
|
|
return Status;
|
|
}
|