NTSTATUS NTAPI PsSuspendThread(IN PETHREAD Thread, OUT PULONG PreviousCount OPTIONAL) { NTSTATUS Status; ULONG OldCount = 0; PAGED_CODE(); /* Guard with SEH because KeSuspendThread can raise an exception */ _SEH2_TRY { /* Check if we're suspending ourselves */ if (Thread == PsGetCurrentThread()) { /* Do the suspend */ OldCount = KeSuspendThread(&Thread->Tcb); /* We are done */ Status = STATUS_SUCCESS; } else { /* Acquire rundown */ if (ExAcquireRundownProtection(&Thread->RundownProtect)) { /* Make sure the thread isn't terminating */ if (Thread->Terminated) { /* Fail */ Status = STATUS_THREAD_IS_TERMINATING; } else { /* Otherwise, do the suspend */ OldCount = KeSuspendThread(&Thread->Tcb); /* Check if it terminated during the suspend */ if (Thread->Terminated) { /* Wake it back up and fail */ KeForceResumeThread(&Thread->Tcb); Status = STATUS_THREAD_IS_TERMINATING; OldCount = 0; } } /* Release rundown protection */ ExReleaseRundownProtection(&Thread->RundownProtect); /* We are done */ Status = STATUS_SUCCESS; } else { /* Thread is terminating */ Status = STATUS_THREAD_IS_TERMINATING; } } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Get the exception code */ Status = _SEH2_GetExceptionCode(); /* Don't fail if we merely couldn't write the handle back */ if (Status != STATUS_SUSPEND_COUNT_EXCEEDED) Status = STATUS_SUCCESS; } _SEH2_END; /* Write back the previous count */ if (PreviousCount) *PreviousCount = OldCount; return Status; }
NTSTATUS NtSuspendThread( IN HANDLE ThreadHandle, OUT PULONG PreviousSuspendCount OPTIONAL ) /*++ Routine Description: This function suspends the target thread, and optionally returns the previous suspend count. Arguments: ThreadHandle - Supplies a handle to the thread object to suspend. PreviousSuspendCount - An optional parameter, that if specified points to a variable that receives the thread's previous suspend count. Return Value: return-value - Description of conditions needed to return value. - or - None. --*/ { PETHREAD Thread; NTSTATUS st; ULONG LocalPreviousSuspendCount; KPROCESSOR_MODE Mode; PAGED_CODE(); try { Mode = KeGetPreviousMode(); if ( Mode != KernelMode ) { if (ARGUMENT_PRESENT(PreviousSuspendCount)) { ProbeForWriteUlong(PreviousSuspendCount); } } } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } st = ObReferenceObjectByHandle( ThreadHandle, THREAD_SUSPEND_RESUME, PsThreadType, Mode, (PVOID *)&Thread, NULL ); if ( !NT_SUCCESS(st) ) { return st; } try { if ( Thread != PsGetCurrentThread() ) { if ( Thread->HasTerminated ) { ObDereferenceObject(Thread); return STATUS_THREAD_IS_TERMINATING; } LocalPreviousSuspendCount = (ULONG) KeSuspendThread(&Thread->Tcb); } else { LocalPreviousSuspendCount = (ULONG) KeSuspendThread(&Thread->Tcb); } ObDereferenceObject(Thread); if (ARGUMENT_PRESENT(PreviousSuspendCount)) *PreviousSuspendCount = LocalPreviousSuspendCount; } except (EXCEPTION_EXECUTE_HANDLER) { st = GetExceptionCode(); // // Either the suspend, or the store could cause an // exception. The store is a partial success, while the // suspend exception is an error // if ( st == STATUS_SUSPEND_COUNT_EXCEEDED ) { ObDereferenceObject(Thread); } else { st = STATUS_SUCCESS; } return st; } return STATUS_SUCCESS; }
NTSTATUS NTAPI PspCreateThread(OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN HANDLE ProcessHandle, IN PEPROCESS TargetProcess, OUT PCLIENT_ID ClientId, IN PCONTEXT ThreadContext, IN PINITIAL_TEB InitialTeb, IN BOOLEAN CreateSuspended, IN PKSTART_ROUTINE StartRoutine OPTIONAL, IN PVOID StartContext OPTIONAL) { HANDLE hThread; PEPROCESS Process; PETHREAD Thread; PTEB TebBase = NULL; KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); NTSTATUS Status, AccessStatus; HANDLE_TABLE_ENTRY CidEntry; ACCESS_STATE LocalAccessState; PACCESS_STATE AccessState = &LocalAccessState; AUX_ACCESS_DATA AuxData; BOOLEAN Result, SdAllocated; PSECURITY_DESCRIPTOR SecurityDescriptor; SECURITY_SUBJECT_CONTEXT SubjectContext; PAGED_CODE(); PSTRACE(PS_THREAD_DEBUG, "ThreadContext: %p TargetProcess: %p ProcessHandle: %p\n", ThreadContext, TargetProcess, ProcessHandle); /* If we were called from PsCreateSystemThread, then we're kernel mode */ if (StartRoutine) PreviousMode = KernelMode; /* Reference the Process by handle or pointer, depending on what we got */ if (ProcessHandle) { /* Normal thread or System Thread */ Status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_CREATE_THREAD, PsProcessType, PreviousMode, (PVOID*)&Process, NULL); PSREFTRACE(Process); } else { /* System thread inside System Process, or Normal Thread with a bug */ if (StartRoutine) { /* Reference the Process by Pointer */ ObReferenceObject(TargetProcess); Process = TargetProcess; Status = STATUS_SUCCESS; } else { /* Fake ObReference returning this */ Status = STATUS_INVALID_HANDLE; } } /* Check for success */ if (!NT_SUCCESS(Status)) return Status; /* Also make sure that User-Mode isn't trying to create a system thread */ if ((PreviousMode != KernelMode) && (Process == PsInitialSystemProcess)) { /* Fail */ ObDereferenceObject(Process); return STATUS_INVALID_HANDLE; } /* Create Thread Object */ Status = ObCreateObject(PreviousMode, PsThreadType, ObjectAttributes, PreviousMode, NULL, sizeof(ETHREAD), 0, 0, (PVOID*)&Thread); if (!NT_SUCCESS(Status)) { /* We failed; dereference the process and exit */ ObDereferenceObject(Process); return Status; } /* Zero the Object entirely */ RtlZeroMemory(Thread, sizeof(ETHREAD)); /* Initialize rundown protection */ ExInitializeRundownProtection(&Thread->RundownProtect); /* Initialize exit code */ Thread->ExitStatus = STATUS_PENDING; /* Set the Process CID */ Thread->ThreadsProcess = Process; Thread->Cid.UniqueProcess = Process->UniqueProcessId; /* Create Cid Handle */ CidEntry.Object = Thread; CidEntry.GrantedAccess = 0; Thread->Cid.UniqueThread = ExCreateHandle(PspCidTable, &CidEntry); if (!Thread->Cid.UniqueThread) { /* We couldn't create the CID, dereference the thread and fail */ ObDereferenceObject(Thread); return STATUS_INSUFFICIENT_RESOURCES; } /* Save the read cluster size */ Thread->ReadClusterSize = MmReadClusterSize; /* Initialize the LPC Reply Semaphore */ KeInitializeSemaphore(&Thread->LpcReplySemaphore, 0, 1); /* Initialize the list heads and locks */ InitializeListHead(&Thread->LpcReplyChain); InitializeListHead(&Thread->IrpList); InitializeListHead(&Thread->PostBlockList); InitializeListHead(&Thread->ActiveTimerListHead); KeInitializeSpinLock(&Thread->ActiveTimerListLock); /* Acquire rundown protection */ if (!ExAcquireRundownProtection (&Process->RundownProtect)) { /* Fail */ ObDereferenceObject(Thread); return STATUS_PROCESS_IS_TERMINATING; } /* Now let the kernel initialize the context */ if (ThreadContext) { /* User-mode Thread, create Teb */ Status = MmCreateTeb(Process, &Thread->Cid, InitialTeb, &TebBase); if (!NT_SUCCESS(Status)) { /* Failed to create the TEB. Release rundown and dereference */ ExReleaseRundownProtection(&Process->RundownProtect); ObDereferenceObject(Thread); return Status; } /* Set the Start Addresses */ Thread->StartAddress = (PVOID)KeGetContextPc(ThreadContext); Thread->Win32StartAddress = (PVOID)KeGetContextReturnRegister(ThreadContext); /* Let the kernel intialize the Thread */ Status = KeInitThread(&Thread->Tcb, NULL, PspUserThreadStartup, NULL, Thread->StartAddress, ThreadContext, TebBase, &Process->Pcb); } else { /* System Thread */ Thread->StartAddress = StartRoutine; PspSetCrossThreadFlag(Thread, CT_SYSTEM_THREAD_BIT); /* Let the kernel intialize the Thread */ Status = KeInitThread(&Thread->Tcb, NULL, PspSystemThreadStartup, StartRoutine, StartContext, NULL, NULL, &Process->Pcb); } /* Check if we failed */ if (!NT_SUCCESS(Status)) { /* Delete the TEB if we had done */ if (TebBase) MmDeleteTeb(Process, TebBase); /* Release rundown and dereference */ ExReleaseRundownProtection(&Process->RundownProtect); ObDereferenceObject(Thread); return Status; } /* Lock the process */ KeEnterCriticalRegion(); ExAcquirePushLockExclusive(&Process->ProcessLock); /* Make sure the proces didn't just die on us */ if (Process->ProcessDelete) goto Quickie; /* Check if the thread was ours, terminated and it was user mode */ if ((Thread->Terminated) && (ThreadContext) && (Thread->ThreadsProcess == Process)) { /* Cleanup, we don't want to start it up and context switch */ goto Quickie; } /* * Insert the Thread into the Process's Thread List * Note, this is the ETHREAD Thread List. It is removed in * ps/kill.c!PspExitThread. */ InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry); Process->ActiveThreads++; /* Start the thread */ KeStartThread(&Thread->Tcb); /* Release the process lock */ ExReleasePushLockExclusive(&Process->ProcessLock); KeLeaveCriticalRegion(); /* Release rundown */ ExReleaseRundownProtection(&Process->RundownProtect); /* Notify WMI */ //WmiTraceProcess(Process, TRUE); //WmiTraceThread(Thread, InitialTeb, TRUE); /* Notify Thread Creation */ PspRunCreateThreadNotifyRoutines(Thread, TRUE); /* Reference ourselves as a keep-alive */ ObReferenceObjectEx(Thread, 2); /* Suspend the Thread if we have to */ if (CreateSuspended) KeSuspendThread(&Thread->Tcb); /* Check if we were already terminated */ if (Thread->Terminated) KeForceResumeThread(&Thread->Tcb); /* Create an access state */ Status = SeCreateAccessStateEx(NULL, ThreadContext ? PsGetCurrentProcess() : Process, &LocalAccessState, &AuxData, DesiredAccess, &PsThreadType->TypeInfo.GenericMapping); if (!NT_SUCCESS(Status)) { /* Access state failed, thread is dead */ PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT); /* If we were suspended, wake it up */ if (CreateSuspended) KeResumeThread(&Thread->Tcb); /* Dispatch thread */ KeReadyThread(&Thread->Tcb); /* Dereference completely to kill it */ ObDereferenceObjectEx(Thread, 2); return Status; } /* Insert the Thread into the Object Manager */ Status = ObInsertObject(Thread, AccessState, DesiredAccess, 0, NULL, &hThread); /* Delete the access state if we had one */ if (AccessState) SeDeleteAccessState(AccessState); /* Check for success */ if (NT_SUCCESS(Status)) { /* Wrap in SEH to protect against bad user-mode pointers */ _SEH2_TRY { /* Return Cid and Handle */ if (ClientId) *ClientId = Thread->Cid; *ThreadHandle = hThread; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { /* Thread insertion failed, thread is dead */ PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT); /* If we were suspended, wake it up */ if (CreateSuspended) KeResumeThread(&Thread->Tcb); /* Dispatch thread */ KeReadyThread(&Thread->Tcb); /* Dereference it, leaving only the keep-alive */ ObDereferenceObject(Thread); /* Close its handle, killing it */ ObCloseHandle(ThreadHandle, PreviousMode); /* Return the exception code */ _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; } else {