Skip to content

Instantly share code, notes, and snippets.

@kuh4it
Last active December 17, 2023 17:08
Show Gist options
  • Save kuh4it/b1dfccd4332891b50eae03e7850a58d3 to your computer and use it in GitHub Desktop.
Save kuh4it/b1dfccd4332891b50eae03e7850a58d3 to your computer and use it in GitHub Desktop.
Create legit kernel system thread
//
// InterDKOM - Making magic happen
// -> Thread.c
//
NTSTATUS
InterDkom::Core::PsCreateLegitSystemThread
(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();
if (StartRoutine) PreviousMode = KernelMode;
if (ProcessHandle)
{
Status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_CREATE_THREAD,
PsProcessType,
PreviousMode,
(PVOID*)&Process,
NULL);
PSREFTRACE(Process);
}
else
{
if (StartRoutine)
{
ObReferenceObject(TargetProcess);
Process = TargetProcess;
Status = STATUS_SUCCESS;
}
else
{
Status = STATUS_INVALID_HANDLE;
}
}
if (!NT_SUCCESS(Status)) return Status;
if ((PreviousMode != KernelMode) && (Process == PsInitialSystemProcess))
{
ObDereferenceObject(Process);
return STATUS_INVALID_HANDLE;
}
Status = ObCreateObject(PreviousMode,
PsThreadType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof(ETHREAD),
0,
0,
(PVOID*)&Thread);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(Process);
return Status;
}
RtlZeroMemory(Thread, sizeof(ETHREAD));
ExInitializeRundownProtection(&Thread->RundownProtect);
Thread->ExitStatus = STATUS_PENDING;
Thread->ThreadsProcess = Process;
Thread->Cid.UniqueProcess = Process->UniqueProcessId;
CidEntry.Object = Thread;
CidEntry.GrantedAccess = 0;
Thread->Cid.UniqueThread = ExCreateHandle(PspCidTable, &CidEntry);
if (!Thread->Cid.UniqueThread)
{
ObDereferenceObject(Thread);
return STATUS_INSUFFICIENT_RESOURCES;
}
Thread->ReadClusterSize = MmReadClusterSize;
KeInitializeSemaphore(&Thread->LpcReplySemaphore, 0, 1);
InitializeListHead(&Thread->LpcReplyChain);
InitializeListHead(&Thread->IrpList);
InitializeListHead(&Thread->PostBlockList);
InitializeListHead(&Thread->ActiveTimerListHead);
KeInitializeSpinLock(&Thread->ActiveTimerListLock);
if (!ExAcquireRundownProtection(&Process->RundownProtect))
{
ObDereferenceObject(Thread);
return STATUS_PROCESS_IS_TERMINATING;
}
if (ThreadContext)
{
Status = MmCreateTeb(Process, &Thread->Cid, InitialTeb, &TebBase);
if (!NT_SUCCESS(Status))
{
ExReleaseRundownProtection(&Process->RundownProtect);
ObDereferenceObject(Thread);
return Status;
}
_SEH2_TRY
{
//
// Meme it ;)
//
Thread->StartAddress = Ctx->SafeMemoryRegion.Thread(3)->StartAddress;
Thread->Win32StartAddress = Ctx->SafeMemoryRegion.Thread(3)->Win32StartAddress;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
if (NT_SUCCESS(Status))
{
Status = KeInitThread(&Thread->Tcb,
NULL,
PspUserThreadStartup,
NULL,
Thread->StartAddress,
ThreadContext,
TebBase,
&Process->Pcb);
}
}
else
{
Thread->StartAddress = StartRoutine;
PspSetCrossThreadFlag(Thread, CT_SYSTEM_THREAD_BIT);
Status = KeInitThread(&Thread->Tcb,
NULL,
PspSystemThreadStartup,
StartRoutine,
StartContext,
NULL,
NULL,
&Process->Pcb);
}
if (!NT_SUCCESS(Status))
{
if (TebBase) MmDeleteTeb(Process, TebBase);
ExReleaseRundownProtection(&Process->RundownProtect);
ObDereferenceObject(Thread);
return Status;
}
KeEnterCriticalRegion();
ExAcquirePushLockExclusive(&Process->ProcessLock);
if (Process->ProcessDelete) goto Quickie;
if ((Thread->Terminated) &&
(ThreadContext) &&
(Thread->ThreadsProcess == Process))
{
goto Quickie;
}
//
// Insertion to ETHREAD list
//
InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
Process->ActiveThreads++;
KeStartThread(&Thread->Tcb);
ExReleasePushLockExclusive(&Process->ProcessLock);
KeLeaveCriticalRegion();
ExReleaseRundownProtection(&Process->RundownProtect);
PspRunCreateThreadNotifyRoutines(Thread, TRUE);
ObReferenceObjectEx(Thread, 2);
if (CreateSuspended) KeSuspendThread(&Thread->Tcb);
if (Thread->Terminated) KeForceResumeThread(&Thread->Tcb);
Status = SeCreateAccessStateEx(NULL,
ThreadContext ?
PsGetCurrentProcess() : Process,
&LocalAccessState,
&AuxData,
DesiredAccess,
&PsThreadType->TypeInfo.GenericMapping);
if (!NT_SUCCESS(Status))
{
PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
if (CreateSuspended) KeResumeThread(&Thread->Tcb);
KeReadyThread(&Thread->Tcb);
ObDereferenceObjectEx(Thread, 2);
return Status;
}
Status = ObInsertObject(Thread,
AccessState,
DesiredAccess,
0,
NULL,
&hThread);
if (AccessState) SeDeleteAccessState(AccessState);
if (NT_SUCCESS(Status))
{
//
// Wrap custom legit memory in SEH
// (Meme it ;)
//
_SEH2_TRY
{
Mislocate(Thread->Cid, Ctx->SafeMemoryRegion, Ctx->Cid);
if (Ctx->Cid) Thread->Cid = Ctx->Cid;
if (ClientId) *ClientId = Thread->Cid;
*ThreadHandle = hThread;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
if (CreateSuspended) KeResumeThread(&Thread->Tcb);
KeReadyThread(&Thread->Tcb);
ObDereferenceObject(Thread);
ObCloseHandle(hThread, PreviousMode);
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
}
else
{
PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
if (CreateSuspended) KeResumeThread(&Thread->Tcb);
}
//
// Meme it ;)
//
&Thread->CreateTime = Ctx->SafeMemoryRegion.Thread(3)->CreateTime;
ASSERT(!(Thread->CreateTime.HighPart & 0xF0000000));
if (!Thread->DeadThread)
{
Status = ObGetObjectSecurity(Thread,
&SecurityDescriptor,
&SdAllocated);
if (!NT_SUCCESS(Status))
{
PspSetCrossThreadFlag(Thread, CT_DEAD_THREAD_BIT);
if (CreateSuspended) KeResumeThread(&Thread->Tcb);
KeReadyThread(&Thread->Tcb);
ObDereferenceObject(Thread);
ObCloseHandle(hThread, PreviousMode);
return Status;
}
SubjectContext.ProcessAuditId = Process;
SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
SubjectContext.ClientToken = NULL;
Result = SeAccessCheck(SecurityDescriptor,
&SubjectContext,
FALSE,
MAXIMUM_ALLOWED,
0,
NULL,
&PsThreadType->TypeInfo.GenericMapping,
PreviousMode,
&Thread->GrantedAccess,
&AccessStatus);
ObFastDereferenceObject(&Process->Token,
SubjectContext.PrimaryToken);
ObReleaseObjectSecurity(SecurityDescriptor, SdAllocated);
if (!Result) Process->GrantedAccess = 0;
Thread->GrantedAccess |= (THREAD_TERMINATE |
THREAD_SET_INFORMATION |
THREAD_QUERY_INFORMATION);
}
else
{
Thread->GrantedAccess = THREAD_ALL_ACCESS;
}
KeReadyThread(&Thread->Tcb);
ObDereferenceObject(Thread);
return Status;
Quickie:
ExReleasePushLockExclusive(&Process->ProcessLock);
KeLeaveCriticalRegion();
KeUninitThread(&Thread->Tcb);
if (TebBase) MmDeleteTeb(Process, TebBase);
ExReleaseRundownProtection(&Process->RundownProtect);
ObDereferenceObject(Thread);
return STATUS_PROCESS_IS_TERMINATING;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment