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;
|
||
}
|