379 lines
9.5 KiB
C
379 lines
9.5 KiB
C
|
/*++
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
context.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements user-mode callable context manipulation routines.
|
|||
|
The interfaces exported from this module are portable, but they must
|
|||
|
be re-implemented for each architecture.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
Ported to the IA64
|
|||
|
|
|||
|
27-Feb-1996 Revised to pass arguments to target thread by injecting
|
|||
|
arguments into the backing store.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ntrtlp.h"
|
|||
|
#include "kxia64.h"
|
|||
|
|
|||
|
#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
|
|||
|
#pragma alloc_text(PAGE,RtlInitializeContext)
|
|||
|
#pragma alloc_text(PAGE,RtlRemoteCall)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
RtlInitializeContext(
|
|||
|
IN HANDLE Process,
|
|||
|
OUT PCONTEXT Context,
|
|||
|
IN PVOID Parameter OPTIONAL,
|
|||
|
IN PVOID InitialPc OPTIONAL,
|
|||
|
IN PVOID InitialSp OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function initializes a context structure so that it can
|
|||
|
be used in a subsequent call to NtCreateThread.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Supplies a context buffer to be initialized by this routine.
|
|||
|
|
|||
|
InitialPc - Supplies an initial program counter value.
|
|||
|
|
|||
|
InitialSp - Supplies an initial stack pointer value.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Raises STATUS_BAD_INITIAL_STACK if the value of InitialSp is not properly
|
|||
|
aligned.
|
|||
|
|
|||
|
Raises STATUS_BAD_INITIAL_PC if the value of InitialPc is not properly
|
|||
|
aligned.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONGLONG Argument;
|
|||
|
ULONG_PTR Wow64Info;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
RTL_PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Check for proper initial stack (0 mod 16).
|
|||
|
//
|
|||
|
|
|||
|
if (((ULONG_PTR)InitialSp & 0xf) != 0) {
|
|||
|
RtlRaiseStatus(STATUS_BAD_INITIAL_STACK);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check for proper plabel address alignment.
|
|||
|
// Assumes InitialPc points to a plabel that must be 8-byte aligned.
|
|||
|
//
|
|||
|
if (((ULONG_PTR)InitialPc & 0x7) != 0) {
|
|||
|
//
|
|||
|
// Misaligned, See if we are running in a Wow64 process
|
|||
|
//
|
|||
|
Status = ZwQueryInformationProcess(Process,
|
|||
|
ProcessWow64Information,
|
|||
|
&Wow64Info,
|
|||
|
sizeof(Wow64Info),
|
|||
|
NULL);
|
|||
|
|
|||
|
if (NT_SUCCESS(Status) && (Wow64Info == 0))
|
|||
|
{
|
|||
|
//
|
|||
|
// Native IA64 process must not be misaligned.
|
|||
|
//
|
|||
|
RtlRaiseStatus(STATUS_BAD_INITIAL_PC);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the integer and floating registers to contain zeroes.
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory(Context, sizeof(CONTEXT));
|
|||
|
|
|||
|
//
|
|||
|
// Setup integer and control context.
|
|||
|
//
|
|||
|
|
|||
|
Context->ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
|
|||
|
|
|||
|
Context->RsBSPSTORE = Context->IntSp = (ULONG_PTR)InitialSp;
|
|||
|
Context->IntSp -= STACK_SCRATCH_AREA;
|
|||
|
|
|||
|
//
|
|||
|
// InitialPc is the module entry point which is a function pointer
|
|||
|
// in IA64. StIIP and IntGp are initiailized with actual IP and GP
|
|||
|
// from the plabel in LdrInitializeThunk after the loader runs.
|
|||
|
//
|
|||
|
|
|||
|
Context->IntS1 = Context->IntS0 = Context->StIIP = (ULONG_PTR)InitialPc;
|
|||
|
Context->IntGp = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Setup FPSR, PSR, and DCR
|
|||
|
// N.B. Values to be determined.
|
|||
|
//
|
|||
|
|
|||
|
Context->StFPSR = USER_FPSR_INITIAL;
|
|||
|
Context->StIPSR = USER_PSR_INITIAL;
|
|||
|
Context->ApDCR = USER_DCR_INITIAL;
|
|||
|
|
|||
|
//
|
|||
|
// Set the initial context of the thread in a machine specific way.
|
|||
|
// ie, pass the initial parameter to the RSE by saving it at the
|
|||
|
// bottom of the backing store.
|
|||
|
//
|
|||
|
// Setup Frame Marker after RFI
|
|||
|
// And other RSE states.
|
|||
|
//
|
|||
|
|
|||
|
Argument = (ULONGLONG)Parameter;
|
|||
|
ZwWriteVirtualMemory(Process,
|
|||
|
(PVOID)((ULONG_PTR)Context->RsBSPSTORE),
|
|||
|
(PVOID)&Argument,
|
|||
|
sizeof(Argument),
|
|||
|
NULL);
|
|||
|
//
|
|||
|
// N.b. The IFS must be reinitialized in LdrInitializeThunk
|
|||
|
//
|
|||
|
|
|||
|
Context->StIFS = 0x8000000000000081ULL; // Valid, 1 local register, 0 output register
|
|||
|
Context->RsBSP = Context->RsBSPSTORE;
|
|||
|
Context->RsRSC = USER_RSC_INITIAL;
|
|||
|
Context->ApUNAT = 0xFFFFFFFFFFFFFFFF;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RtlRemoteCall(
|
|||
|
HANDLE Process,
|
|||
|
HANDLE Thread,
|
|||
|
PVOID CallSite,
|
|||
|
ULONG ArgumentCount,
|
|||
|
PULONG_PTR Arguments,
|
|||
|
BOOLEAN PassContext,
|
|||
|
BOOLEAN AlreadySuspended
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function calls a procedure in another thread/process, using
|
|||
|
NtGetContext and NtSetContext. Parameters are passed to the
|
|||
|
target procedure via its stack.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Process - Handle of the target process
|
|||
|
|
|||
|
Thread - Handle of the target thread within that process
|
|||
|
|
|||
|
CallSite - Address of the procedure to call in the target process.
|
|||
|
|
|||
|
ArgumentCount - Number of parameters to pass to the target
|
|||
|
procedure.
|
|||
|
|
|||
|
Arguments - Pointer to the array of parameters to pass.
|
|||
|
|
|||
|
PassContext - TRUE if an additional parameter is to be passed that
|
|||
|
points to a context record.
|
|||
|
|
|||
|
AlreadySuspended - TRUE if the target thread is already in a suspended
|
|||
|
or waiting state.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status - Status value
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
CONTEXT Context;
|
|||
|
ULONG_PTR ContextAddress;
|
|||
|
ULONG_PTR NewSp;
|
|||
|
ULONG_PTR NewBsp;
|
|||
|
ULONGLONG ArgumentsCopy[9];
|
|||
|
PVOID ptr;
|
|||
|
ULONG ShiftCount;
|
|||
|
SHORT RNatSaveIndex, TotalFrameSize, Temp;
|
|||
|
BOOLEAN RnatSaved = FALSE;
|
|||
|
ULONG Count = 0;
|
|||
|
|
|||
|
|
|||
|
RTL_PAGED_CODE();
|
|||
|
|
|||
|
if ((ArgumentCount > 8) || (PassContext && (ArgumentCount > 7))) {
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If necessary, suspend the guy before with we mess with his stack.
|
|||
|
//
|
|||
|
|
|||
|
if (AlreadySuspended == FALSE) {
|
|||
|
Status = NtSuspendThread(Thread, NULL);
|
|||
|
if (NT_SUCCESS(Status) == FALSE) {
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the context record of the target thread.
|
|||
|
//
|
|||
|
|
|||
|
Context.ContextFlags = CONTEXT_FULL;
|
|||
|
Status = NtGetContextThread(Thread, &Context);
|
|||
|
if (NT_SUCCESS(Status) == FALSE) {
|
|||
|
if (AlreadySuspended == FALSE) {
|
|||
|
NtResumeThread(Thread, NULL);
|
|||
|
}
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
if (AlreadySuspended) {
|
|||
|
Context.IntV0 = STATUS_ALERTED;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Pass the parameters to the other thread via the backing store (r32-r39).
|
|||
|
// The context record is passed on the stack of the target thread.
|
|||
|
// N.B. Align the context record address, stack pointer, and allocate
|
|||
|
// stack scratch area.
|
|||
|
//
|
|||
|
|
|||
|
ContextAddress = (((ULONG_PTR)Context.IntSp + 0xf) & ~0xfi64) - sizeof(CONTEXT);
|
|||
|
NewSp = ContextAddress - STACK_SCRATCH_AREA;
|
|||
|
Status = NtWriteVirtualMemory(Process, (PVOID)ContextAddress, &Context,
|
|||
|
sizeof(CONTEXT), NULL);
|
|||
|
|
|||
|
if (NT_SUCCESS(Status) == FALSE) {
|
|||
|
if (AlreadySuspended == FALSE) {
|
|||
|
NtResumeThread(Thread, NULL);
|
|||
|
}
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
RtlZeroMemory((PVOID)ArgumentsCopy, sizeof(ArgumentsCopy));
|
|||
|
|
|||
|
TotalFrameSize = (SHORT)(Context.StIFS & PFS_SIZE_MASK);
|
|||
|
RNatSaveIndex = (SHORT)((Context.RsBSP >> 3) & NAT_BITS_PER_RNAT_REG);
|
|||
|
Temp = RNatSaveIndex + TotalFrameSize - NAT_BITS_PER_RNAT_REG;
|
|||
|
while (Temp >= 0) {
|
|||
|
TotalFrameSize++;
|
|||
|
Temp -= NAT_BITS_PER_RNAT_REG;
|
|||
|
}
|
|||
|
NewBsp = Context.RsBSP + TotalFrameSize * sizeof(ULONGLONG);
|
|||
|
Context.RsBSP = NewBsp;
|
|||
|
|
|||
|
if (PassContext) {
|
|||
|
ShiftCount = (ULONG) (NewBsp & 0x1F8) >> 3;
|
|||
|
Context.RsRNAT &= ~(0x1i64 << ShiftCount);
|
|||
|
ArgumentsCopy[Count++] = ContextAddress;
|
|||
|
NewBsp += sizeof(ULONGLONG);
|
|||
|
}
|
|||
|
|
|||
|
for (; ArgumentCount != 0 ; ArgumentCount--) {
|
|||
|
if ((NewBsp & 0x1F8) == 0x1F8) {
|
|||
|
ArgumentsCopy[Count++] = Context.RsRNAT;
|
|||
|
Context.RsRNAT = -1i64;
|
|||
|
NewBsp += sizeof(ULONGLONG);
|
|||
|
}
|
|||
|
ShiftCount = (ULONG)(NewBsp & 0x1F8) >> 3;
|
|||
|
Context.RsRNAT &= ~(0x1i64 << ShiftCount);
|
|||
|
ArgumentsCopy[Count++] = (ULONGLONG)(*Arguments++);
|
|||
|
NewBsp += sizeof(ULONGLONG);
|
|||
|
}
|
|||
|
|
|||
|
if ((NewBsp & 0x1F8) == 0x1F8) {
|
|||
|
ArgumentsCopy[Count++] = Context.RsRNAT;
|
|||
|
Context.RsRNAT = -1i64;
|
|||
|
NewBsp += sizeof(ULONGLONG);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy the arguments onto the target backing store.
|
|||
|
//
|
|||
|
|
|||
|
if (Count) {
|
|||
|
Status = NtWriteVirtualMemory(Process,
|
|||
|
(PVOID)Context.RsBSP,
|
|||
|
ArgumentsCopy,
|
|||
|
Count * sizeof(ULONGLONG),
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(Status) == FALSE) {
|
|||
|
if (AlreadySuspended == FALSE) {
|
|||
|
NtResumeThread(Thread, NULL);
|
|||
|
}
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// set up RSE
|
|||
|
//
|
|||
|
|
|||
|
Context.RsRSC = (RSC_MODE_LY<<RSC_MODE)
|
|||
|
| (RSC_BE_LITTLE<<RSC_BE)
|
|||
|
| (0x3<<RSC_PL);
|
|||
|
|
|||
|
Count = ArgumentCount + (PassContext ? 1 : 0);
|
|||
|
|
|||
|
//
|
|||
|
// Inject all arguments as local stack registers in the target RSE frame
|
|||
|
//
|
|||
|
|
|||
|
Context.StIFS = (0x3i64 << 62) | Count | (Count << PFS_SIZE_SHIFT);
|
|||
|
|
|||
|
//
|
|||
|
// Set the address of the target code into IIP, the new target stack
|
|||
|
// into sp, setup ap, and reload context to make it happen.
|
|||
|
//
|
|||
|
|
|||
|
Context.IntSp = (ULONG_PTR)NewSp;
|
|||
|
|
|||
|
//
|
|||
|
// Set IP to the target call site PLABEL and GP to zero. IIP and GP
|
|||
|
// will be computed inside NtSetContextThread.
|
|||
|
//
|
|||
|
|
|||
|
Context.StIIP = (ULONGLONG)CallSite;
|
|||
|
Context.IntGp = 0;
|
|||
|
|
|||
|
//
|
|||
|
// sanitize the floating pointer status register
|
|||
|
//
|
|||
|
|
|||
|
SANITIZE_FSR(Context.StFPSR, UserMode);
|
|||
|
|
|||
|
Status = NtSetContextThread(Thread, &Context);
|
|||
|
if (!AlreadySuspended) {
|
|||
|
NtResumeThread(Thread, NULL);
|
|||
|
}
|
|||
|
|
|||
|
return( Status );
|
|||
|
}
|
|||
|
|