VOID NTAPI PspDeleteThread(IN PVOID ObjectBody) { PETHREAD Thread = (PETHREAD)ObjectBody; PEPROCESS Process = Thread->ThreadsProcess; PAGED_CODE(); PSTRACE(PS_KILL_DEBUG, "ObjectBody: %p\n", ObjectBody); PSREFTRACE(Thread); ASSERT(Thread->Tcb.Win32Thread == NULL); /* Check if we have a stack */ if (Thread->Tcb.InitialStack) { /* Release it */ MmDeleteKernelStack((PVOID)Thread->Tcb.StackBase, Thread->Tcb.LargeStack); } /* Check if we have a CID Handle */ if (Thread->Cid.UniqueThread) { /* Delete the CID Handle */ if (!(ExDestroyHandle(PspCidTable, Thread->Cid.UniqueThread, NULL))) { /* Something wrong happened, bugcheck */ KeBugCheck(CID_HANDLE_DELETION); } } /* Cleanup impersionation information */ PspDeleteThreadSecurity(Thread); /* Make sure the thread was inserted, before continuing */ if (!Process) return; /* Check if the thread list is valid */ if (Thread->ThreadListEntry.Flink) { /* Lock the thread's process */ KeEnterCriticalRegion(); ExAcquirePushLockExclusive(&Process->ProcessLock); /* Remove us from the list */ RemoveEntryList(&Thread->ThreadListEntry); /* Release the lock */ ExReleasePushLockExclusive(&Process->ProcessLock); KeLeaveCriticalRegion(); } /* Dereference the Process */ ObDereferenceObject(Process); }
NTSTATUS NTAPI PspTerminateProcess(IN PEPROCESS Process, IN NTSTATUS ExitStatus) { PETHREAD Thread; NTSTATUS Status = STATUS_NOTHING_TO_TERMINATE; PAGED_CODE(); PSTRACE(PS_KILL_DEBUG, "Process: %p ExitStatus: %d\n", Process, ExitStatus); PSREFTRACE(Process); /* Check if this is a Critical Process */ if (Process->BreakOnTermination) { /* Break to debugger */ PspCatchCriticalBreak("Terminating critical process 0x%p (%s)\n", Process, Process->ImageFileName); } /* Set the delete flag */ InterlockedOr((PLONG)&Process->Flags, PSF_PROCESS_DELETE_BIT); /* Get the first thread */ Thread = PsGetNextProcessThread(Process, NULL); while (Thread) { /* Kill it */ PspTerminateThreadByPointer(Thread, ExitStatus, FALSE); Thread = PsGetNextProcessThread(Process, Thread); /* We had at least one thread, so termination is OK */ Status = STATUS_SUCCESS; } /* Check if there was nothing to terminate or if we have a debug port */ if ((Status == STATUS_NOTHING_TO_TERMINATE) || (Process->DebugPort)) { /* Clear the handle table anyway */ ObClearProcessHandleTable(Process); } /* Return status */ return Status; }
/* * See "Windows Internals" - Chapter 13, Page 49 */ NTSTATUS NTAPI PspTerminateThreadByPointer(IN PETHREAD Thread, IN NTSTATUS ExitStatus, IN BOOLEAN bSelf) { PKAPC Apc; NTSTATUS Status = STATUS_SUCCESS; ULONG Flags; PAGED_CODE(); PSTRACE(PS_KILL_DEBUG, "Thread: %p ExitStatus: %d\n", Thread, ExitStatus); PSREFTRACE(Thread); /* Check if this is a Critical Thread, and Bugcheck */ if (Thread->BreakOnTermination) { /* Break to debugger */ PspCatchCriticalBreak("Terminating critical thread 0x%p (%s)\n", Thread, Thread->ThreadsProcess->ImageFileName); } /* Check if we are already inside the thread */ if ((bSelf) || (PsGetCurrentThread() == Thread)) { /* This should only happen at passive */ ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); /* Mark it as terminated */ PspSetCrossThreadFlag(Thread, CT_TERMINATED_BIT); /* Directly terminate the thread */ PspExitThread(ExitStatus); } /* This shouldn't be a system thread */ if (Thread->SystemThread) return STATUS_ACCESS_DENIED; /* Allocate the APC */ Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC); if (!Apc) return STATUS_INSUFFICIENT_RESOURCES; /* Set the Terminated Flag */ Flags = Thread->CrossThreadFlags | CT_TERMINATED_BIT; /* Set it, and check if it was already set while we were running */ if (!(InterlockedExchange((PLONG)&Thread->CrossThreadFlags, Flags) & CT_TERMINATED_BIT)) { /* Initialize a Kernel Mode APC to Kill the Thread */ KeInitializeApc(Apc, &Thread->Tcb, OriginalApcEnvironment, PsExitSpecialApc, PspExitApcRundown, PspExitNormalApc, KernelMode, (PVOID)ExitStatus); /* Insert it into the APC Queue */ if (!KeInsertQueueApc(Apc, Apc, NULL, 2)) { /* The APC was already in the queue, fail */ Status = STATUS_UNSUCCESSFUL; } else { /* Forcefully resume the thread and return */ KeForceResumeThread(&Thread->Tcb); return Status; } } /* We failed, free the APC */ ExFreePoolWithTag(Apc, TAG_TERMINATE_APC); /* Return Status */ return Status; }
VOID NTAPI PspDeleteProcess(IN PVOID ObjectBody) { PEPROCESS Process = (PEPROCESS)ObjectBody; KAPC_STATE ApcState; PAGED_CODE(); PSTRACE(PS_KILL_DEBUG, "ObjectBody: %p\n", ObjectBody); PSREFTRACE(Process); /* Check if it has an Active Process Link */ if (Process->ActiveProcessLinks.Flink) { /* Remove it from the Active List */ KeAcquireGuardedMutex(&PspActiveProcessMutex); RemoveEntryList(&Process->ActiveProcessLinks); Process->ActiveProcessLinks.Flink = NULL; Process->ActiveProcessLinks.Blink = NULL; KeReleaseGuardedMutex(&PspActiveProcessMutex); } /* Check for Auditing information */ if (Process->SeAuditProcessCreationInfo.ImageFileName) { /* Free it */ ExFreePoolWithTag(Process->SeAuditProcessCreationInfo.ImageFileName, TAG_SEPA); Process->SeAuditProcessCreationInfo.ImageFileName = NULL; } /* Check if we have a job */ if (Process->Job) { /* Remove the process from the job */ PspRemoveProcessFromJob(Process, Process->Job); /* Dereference it */ ObDereferenceObject(Process->Job); Process->Job = NULL; } /* Increase the stack count */ Process->Pcb.StackCount++; /* Check if we have a debug port */ if (Process->DebugPort) { /* Deference the Debug Port */ ObDereferenceObject(Process->DebugPort); Process->DebugPort = NULL; } /* Check if we have an exception port */ if (Process->ExceptionPort) { /* Deference the Exception Port */ ObDereferenceObject(Process->ExceptionPort); Process->ExceptionPort = NULL; } /* Check if we have a section object */ if (Process->SectionObject) { /* Deference the Section Object */ ObDereferenceObject(Process->SectionObject); Process->SectionObject = NULL; } #if defined(_X86_) /* Clean Ldt and Vdm objects */ PspDeleteLdt(Process); PspDeleteVdmObjects(Process); #endif /* Delete the Object Table */ if (Process->ObjectTable) { /* Attach to the process */ KeStackAttachProcess(&Process->Pcb, &ApcState); /* Kill the Object Info */ ObKillProcess(Process); /* Detach */ KeUnstackDetachProcess(&ApcState); } /* Check if we have an address space, and clean it */ if (Process->HasAddressSpace) { /* Attach to the process */ KeStackAttachProcess(&Process->Pcb, &ApcState); /* Clean the Address Space */ PspExitProcess(FALSE, Process); /* Detach */ KeUnstackDetachProcess(&ApcState); /* Completely delete the Address Space */ MmDeleteProcessAddressSpace(Process); } /* See if we have a PID */ if (Process->UniqueProcessId) { /* Delete the PID */ if (!(ExDestroyHandle(PspCidTable, Process->UniqueProcessId, NULL))) { /* Something wrong happened, bugcheck */ KeBugCheck(CID_HANDLE_DELETION); } } /* Cleanup security information */ PspDeleteProcessSecurity(Process); /* Check if we have kept information on the Working Set */ if (Process->WorkingSetWatch) { /* Free it */ ExFreePool(Process->WorkingSetWatch); /* And return the quota it was taking up */ PsReturnProcessNonPagedPoolQuota(Process, 0x2000); } /* Dereference the Device Map */ ObDereferenceDeviceMap(Process); /* Destroy the Quota Block */ PspDestroyQuotaBlock(Process); }
VOID NTAPI PspExitProcess(IN BOOLEAN LastThread, IN PEPROCESS Process) { ULONG Actual; PAGED_CODE(); PSTRACE(PS_KILL_DEBUG, "LastThread: %u Process: %p\n", LastThread, Process); PSREFTRACE(Process); /* Set Process Exit flag */ InterlockedOr((PLONG)&Process->Flags, PSF_PROCESS_EXITING_BIT); /* Check if we are the last thread */ if (LastThread) { /* Notify the WMI Process Callback */ //WmiTraceProcess(Process, FALSE); /* Run the Notification Routines */ PspRunCreateProcessNotifyRoutines(Process, FALSE); } /* Cleanup the power state */ PopCleanupPowerState((PPOWER_STATE)&Process->Pcb.PowerState); /* Clear the security port */ if (!Process->SecurityPort) { /* So we don't double-dereference */ Process->SecurityPort = (PVOID)1; } else if (Process->SecurityPort != (PVOID)1) { /* Dereference it */ ObDereferenceObject(Process->SecurityPort); Process->SecurityPort = (PVOID)1; } /* Check if we are the last thread */ if (LastThread) { /* Check if we have to set the Timer Resolution */ if (Process->SetTimerResolution) { /* Set it to default */ ZwSetTimerResolution(KeMaximumIncrement, 0, &Actual); } /* Check if we are part of a Job that has a completion port */ if ((Process->Job) && (Process->Job->CompletionPort)) { /* FIXME: Check job status code and do I/O completion if needed */ } /* FIXME: Notify the Prefetcher */ } else { /* Clear process' address space here */ MmCleanProcessAddressSpace(Process); } }
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 from the untrusted ThreadContext */ _SEH2_TRY { Thread->StartAddress = (PVOID)KeGetContextPc(ThreadContext); Thread->Win32StartAddress = (PVOID)KeGetContextReturnRegister(ThreadContext); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; /* Let the kernel intialize the Thread */ if (NT_SUCCESS(Status)) { Status = KeInitThread(&Thread->Tcb, NULL, PspUserThreadStartup, NULL, Thread->StartAddress, ThreadContext, TebBase, &Process->Pcb); } } else {
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 {