505 lines
14 KiB
C
505 lines
14 KiB
C
|
/*++
|
|||
|
|
|||
|
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, 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();
|
|||
|
|
|||
|
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;
|
|||
|
KIRQL Irql;
|
|||
|
PKAPC Apc;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
Mode = KeGetPreviousMode();
|
|||
|
|
|||
|
st = ObReferenceObjectByHandle(
|
|||
|
ThreadHandle,
|
|||
|
THREAD_SET_CONTEXT,
|
|||
|
PsThreadType,
|
|||
|
Mode,
|
|||
|
(PVOID *)&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 ) {
|
|||
|
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
|
|||
|
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.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
ULONG Alignment;
|
|||
|
ULONG ContextFlags;
|
|||
|
GETSETCONTEXT ContextFrame;
|
|||
|
ULONG ContextLength;
|
|||
|
KIRQL Irql;
|
|||
|
KPROCESSOR_MODE Mode;
|
|||
|
NTSTATUS Status;
|
|||
|
PETHREAD Thread;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Get previous mode and reference specified thread.
|
|||
|
//
|
|||
|
|
|||
|
Mode = KeGetPreviousMode();
|
|||
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|||
|
THREAD_GET_CONTEXT,
|
|||
|
PsThreadType,
|
|||
|
Mode,
|
|||
|
(PVOID *)&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) {
|
|||
|
|
|||
|
//
|
|||
|
// 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.
|
|||
|
//
|
|||
|
|
|||
|
Alignment = CONTEXT_ALIGN;
|
|||
|
ContextFlags = ProbeAndReadUlong(&ThreadContext->ContextFlags);
|
|||
|
ContextLength = sizeof(CONTEXT);
|
|||
|
|
|||
|
#if defined(_MIPS_)
|
|||
|
|
|||
|
//
|
|||
|
// The following code is included for backward compatibility
|
|||
|
// with old code that does not understand extended context
|
|||
|
// records on MIPS systems.
|
|||
|
//
|
|||
|
|
|||
|
if ((ContextFlags & CONTEXT_EXTENDED_INTEGER) != CONTEXT_EXTENDED_INTEGER) {
|
|||
|
Alignment = sizeof(ULONG);
|
|||
|
ContextLength = FIELD_OFFSET(CONTEXT, ContextFlags) + 4;
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
if (Mode != KernelMode) {
|
|||
|
ProbeForWrite(ThreadContext, ContextLength, Alignment);
|
|||
|
}
|
|||
|
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
Status = GetExceptionCode();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If an exception did not occur during the probe of the thread
|
|||
|
// context, then get the context of the target thread.
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
KeInitializeEvent(&ContextFrame.OperationComplete,
|
|||
|
NotificationEvent,
|
|||
|
FALSE);
|
|||
|
|
|||
|
ContextFrame.Context.ContextFlags = ContextFlags;
|
|||
|
ContextFrame.Mode = Mode;
|
|||
|
if (Thread == PsGetCurrentThread()) {
|
|||
|
ContextFrame.Apc.SystemArgument1 = NULL;
|
|||
|
ContextFrame.Apc.SystemArgument2 = Thread;
|
|||
|
KeRaiseIrql(APC_LEVEL, &Irql);
|
|||
|
PspGetSetContextSpecialApc(&ContextFrame.Apc,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&ContextFrame.Apc.SystemArgument1,
|
|||
|
&ContextFrame.Apc.SystemArgument2);
|
|||
|
|
|||
|
KeLowerIrql(Irql);
|
|||
|
|
|||
|
//
|
|||
|
// Move context to specfied context record. If an exception
|
|||
|
// occurs, then silently handle it and return success.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
RtlMoveMemory(ThreadContext,
|
|||
|
&ContextFrame.Context,
|
|||
|
ContextLength);
|
|||
|
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
}
|
|||
|
|
|||
|
} 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 {
|
|||
|
RtlMoveMemory(ThreadContext,
|
|||
|
&ContextFrame.Context,
|
|||
|
ContextLength);
|
|||
|
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
Status = STATUS_INVALID_HANDLE;
|
|||
|
}
|
|||
|
|
|||
|
ObDereferenceObject(Thread);
|
|||
|
}
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
ULONG Alignment;
|
|||
|
ULONG ContextFlags;
|
|||
|
GETSETCONTEXT ContextFrame;
|
|||
|
ULONG ContextLength;
|
|||
|
KIRQL Irql;
|
|||
|
KPROCESSOR_MODE Mode;
|
|||
|
NTSTATUS Status;
|
|||
|
PETHREAD Thread;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Get previous mode and reference specified thread.
|
|||
|
//
|
|||
|
|
|||
|
Mode = KeGetPreviousMode();
|
|||
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|||
|
THREAD_SET_CONTEXT,
|
|||
|
PsThreadType,
|
|||
|
Mode,
|
|||
|
(PVOID *)&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) {
|
|||
|
|
|||
|
//
|
|||
|
// 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.
|
|||
|
//
|
|||
|
|
|||
|
Alignment = CONTEXT_ALIGN;
|
|||
|
ContextFlags = ProbeAndReadUlong(&ThreadContext->ContextFlags);
|
|||
|
ContextLength = sizeof(CONTEXT);
|
|||
|
|
|||
|
#if defined(_MIPS_)
|
|||
|
|
|||
|
//
|
|||
|
// The following code is included for backward compatibility
|
|||
|
// with old code that does not understand extended context
|
|||
|
// records on MIPS systems.
|
|||
|
//
|
|||
|
|
|||
|
if ((ContextFlags & CONTEXT_EXTENDED_INTEGER) != CONTEXT_EXTENDED_INTEGER) {
|
|||
|
Alignment = sizeof(ULONG);
|
|||
|
ContextLength = FIELD_OFFSET(CONTEXT, ContextFlags) + 4;
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
if (Mode != KernelMode) {
|
|||
|
ProbeForRead(ThreadContext, ContextLength, Alignment);
|
|||
|
}
|
|||
|
|
|||
|
RtlMoveMemory(&ContextFrame.Context, ThreadContext, ContextLength);
|
|||
|
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
Status = GetExceptionCode();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If an exception did not occur during the probe of the thread
|
|||
|
// context, then set the context of the target thread.
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
KeInitializeEvent(&ContextFrame.OperationComplete,
|
|||
|
NotificationEvent,
|
|||
|
FALSE);
|
|||
|
|
|||
|
ContextFrame.Context.ContextFlags = ContextFlags;
|
|||
|
ContextFrame.Mode = Mode;
|
|||
|
if (Thread == PsGetCurrentThread()) {
|
|||
|
ContextFrame.Apc.SystemArgument1 = (PVOID)1;
|
|||
|
ContextFrame.Apc.SystemArgument2 = Thread;
|
|||
|
KeRaiseIrql(APC_LEVEL, &Irql);
|
|||
|
PspGetSetContextSpecialApc(&ContextFrame.Apc,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&ContextFrame.Apc.SystemArgument1,
|
|||
|
&ContextFrame.Apc.SystemArgument2);
|
|||
|
|
|||
|
KeLowerIrql(Irql);
|
|||
|
|
|||
|
} 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);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
Status = STATUS_INVALID_HANDLE;
|
|||
|
}
|
|||
|
|
|||
|
ObDereferenceObject(Thread);
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|