mirror of https://github.com/lianthony/NT4.0
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.
256 lines
6.9 KiB
256 lines
6.9 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
xtlexec.c
|
|
|
|
Abstract:
|
|
|
|
User Mode routines for creating user mode processes and threads.
|
|
|
|
Author:
|
|
|
|
Steve Wood (stevewo) 18-Aug-1989
|
|
|
|
Revision History:
|
|
|
|
Yaron Shamir (YaronS) 18-June-1991
|
|
Cut a lot and modify to the needs of os2ss. User mode only
|
|
|
|
--*/
|
|
|
|
#define INCL_OS2V20_TASKING
|
|
#define INCL_OS2V20_ERRORS
|
|
#include "os2srv.h"
|
|
#include <nturtl.h>
|
|
#define NTOS2_ONLY
|
|
#include "sesport.h"
|
|
#include "os2tile.h"
|
|
|
|
|
|
NTSTATUS
|
|
XtlRemoteCall(
|
|
HANDLE Process,
|
|
HANDLE Thread,
|
|
PVOID CallSite,
|
|
ULONG ArgumentCount,
|
|
PULONG Arguments,
|
|
BOOLEAN PassContext,
|
|
BOOLEAN AlreadySuspended,
|
|
PVOID CritSectionAddr
|
|
)
|
|
|
|
/*++
|
|
|
|
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 32 bit parameters to pass to the target
|
|
procedure.
|
|
|
|
Arguments - Pointer to the array of 32 bit 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.
|
|
|
|
CritSectionAddr - Address of critical section of code that we should
|
|
not suspend process in.
|
|
|
|
Return Value:
|
|
|
|
Status - Status value
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
CONTEXT Context;
|
|
ULONG NewSp;
|
|
LARGE_INTEGER timeout;
|
|
PLARGE_INTEGER ptimeout = &timeout;
|
|
ULONG Count;
|
|
ULONG ArgumentsCopy[5];
|
|
ULONG SuspendCount;
|
|
|
|
if (ArgumentCount > 4)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// If necessary, suspend the guy before with we mess with his stack.
|
|
//
|
|
if (!AlreadySuspended) {
|
|
Status = NtSuspendThread(Thread, NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return( Status );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the context record for the target thread.
|
|
//
|
|
Count = 0;
|
|
while (TRUE) {
|
|
Context.ContextFlags = CONTEXT_FULL;
|
|
Status = NtGetContextThread(Thread, &Context);
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (!AlreadySuspended) {
|
|
NtResumeThread(Thread, NULL);
|
|
}
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(Status);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check that process context not in the middle of thunking
|
|
// between 16-bit & 32-bit code. We can not be any were between
|
|
// EntryFlat and the start of the first thunk. That means we can not
|
|
// be in the ExitFlat code. But we can be in the code for any of the
|
|
// thunks or beyond "Od2SignalDeliverer"
|
|
//
|
|
if ((Context.SegCs != 0x1b) || (Context.SegSs == 0x23)){
|
|
|
|
/*
|
|
(Context.Eip < (ULONG) CritSectionAddr) ||
|
|
(Context.Eip > (ULONG) CritSectionAddr + 0x66C0)) {
|
|
*/
|
|
if (Context.SegCs == 0x1b) {
|
|
Context.SegSs = Context.SegDs = Context.SegEs = 0x23;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// At this point process is in thunk see if it was suspended, if it
|
|
// is not much we can do.
|
|
//
|
|
if (AlreadySuspended) {
|
|
ASSERT(FALSE);
|
|
return(TRUE);
|
|
}
|
|
|
|
do {
|
|
NtResumeThread(Thread, &SuspendCount);
|
|
} while (SuspendCount);
|
|
|
|
//
|
|
// Let process run.
|
|
//
|
|
timeout.LowPart = 0x989680; // wait one second
|
|
timeout.HighPart = 0;
|
|
NtWaitForSingleObject(Thread, (BOOLEAN)TRUE, ptimeout);
|
|
|
|
if (Count++ == 10) {
|
|
ASSERT(FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Try again
|
|
//
|
|
Status = NtSuspendThread(Thread, NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Pass all parameters on the stack, regardless of whether a
|
|
// a context record is passed.
|
|
//
|
|
|
|
//
|
|
// Put Context Record on stack first, so it is above other args.
|
|
//
|
|
if (Context.SegSs != 0x23) {
|
|
NewSp = ((ULONG)(SELTOFLAT(Context.SegSs)) + (USHORT) Context.Esp) & 0xfffffffc;
|
|
}
|
|
else {
|
|
NewSp = Context.Esp;
|
|
}
|
|
|
|
if (PassContext) {
|
|
NewSp -= sizeof( CONTEXT );
|
|
Status = NtWriteVirtualMemory( Process,
|
|
(PVOID)NewSp,
|
|
&Context,
|
|
sizeof( CONTEXT ),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
if (!AlreadySuspended) {
|
|
NtResumeThread( Thread, NULL );
|
|
}
|
|
return( Status );
|
|
}
|
|
ArgumentsCopy[0] = NewSp; // pass pointer to context
|
|
RtlMoveMemory(&(ArgumentsCopy[1]),Arguments,ArgumentCount*sizeof( ULONG ));
|
|
ArgumentCount++;
|
|
}
|
|
else {
|
|
RtlMoveMemory(ArgumentsCopy,Arguments,ArgumentCount*sizeof( ULONG ));
|
|
}
|
|
|
|
//
|
|
// Copy the arguments onto the target stack
|
|
//
|
|
if (ArgumentCount) {
|
|
NewSp -= ArgumentCount * sizeof( ULONG );
|
|
Status = NtWriteVirtualMemory( Process,
|
|
(PVOID)NewSp,
|
|
ArgumentsCopy,
|
|
ArgumentCount * sizeof( ULONG ),
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
if (!AlreadySuspended) {
|
|
NtResumeThread( Thread, NULL );
|
|
}
|
|
return( Status );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the address of the target code into Eip, the new target stack
|
|
// into Esp, and reload context to make it happen.
|
|
//
|
|
Context.SegSs = Context.SegDs = Context.SegEs = 0x23;
|
|
Context.SegCs = 0x1b;
|
|
Context.Esp = NewSp;
|
|
Context.Eip = (ULONG)CallSite;
|
|
Status = NtSetContextThread( Thread, &Context );
|
|
|
|
// Don't resume the thread here. It will be resumed by the caller. The
|
|
// reason for this is that that sometimes caller wan't to perform some
|
|
// operations on suspended thread. For example it might be want to alert
|
|
// the thread. In such cases (signal deliverer is the case, actually)
|
|
// the thread will call to NtTestAlert in order to remove alert flag
|
|
// just in the case that it wasn't waiting for any alertable object.
|
|
// If the thread will be not suspended, the call to NtTestAlert might be
|
|
// executed before the server will alert it. Further, it will cause that
|
|
// for the first time the thread will wait on any alertable object
|
|
// NtWaitForSingleObject will return STATUS_ALERT (not good, but this is
|
|
// NT bug).
|
|
//if (!AlreadySuspended) {
|
|
// NtResumeThread( Thread, NULL );
|
|
//}
|
|
|
|
return( Status );
|
|
}
|
|
|