Files
PYAS/Plugins/Filter/ProtectProcess.cpp
2026-07-07 02:10:56 +08:00

286 lines
9.4 KiB
C++

#include "DriverCommon.h"
constexpr auto PROCESS_RELATION_BUCKET_COUNT = 1024;
constexpr auto PROCESS_RELATION_BUCKET_WAYS = 4;
typedef HANDLE(NTAPI* PPS_GET_PROCESS_INHERITED_FROM_UNIQUE_PROCESS_ID)(PEPROCESS Process);
typedef struct _PROCESS_RELATION_ENTRY {
HANDLE ProcessId;
HANDLE ParentProcessId;
HANDLE CreatorProcessId;
LARGE_INTEGER CreateTime;
LARGE_INTEGER ParentCreateTime;
ULONGLONG Sequence;
} PROCESS_RELATION_ENTRY, * PPROCESS_RELATION_ENTRY;
static PROCESS_RELATION_ENTRY g_ProcessRelations[PROCESS_RELATION_BUCKET_COUNT][PROCESS_RELATION_BUCKET_WAYS];
static KSPIN_LOCK g_ProcessRelationLock;
static EX_RUNDOWN_REF g_ProcessRundown;
static volatile LONG g_ProcessStopping = 1;
static volatile LONG64 g_ProcessRelationSequence = 0;
static BOOLEAN g_ProcessProtectionInitialized = FALSE;
static PPS_GET_PROCESS_INHERITED_FROM_UNIQUE_PROCESS_ID g_GetInheritedProcessId = NULL;
static ULONG GetProcessRelationBucket(HANDLE ProcessId) {
return ((ULONG)(ULONG_PTR)ProcessId >> 2) & (PROCESS_RELATION_BUCKET_COUNT - 1);
}
static BOOLEAN QueryProcessCreateTime(HANDLE ProcessId, PLARGE_INTEGER CreateTime) {
if (CreateTime) CreateTime->QuadPart = 0;
if (!ProcessId || !CreateTime || KeGetCurrentIrql() > APC_LEVEL) return FALSE;
PEPROCESS Process = NULL;
NTSTATUS Status = PsLookupProcessByProcessId(ProcessId, &Process);
if (!NT_SUCCESS(Status)) return FALSE;
CreateTime->QuadPart = PsGetProcessCreateTimeQuadPart(Process);
ObDereferenceObject(Process);
return TRUE;
}
static VOID StoreProcessRelation(
PEPROCESS Process,
HANDLE ProcessId,
HANDLE ParentProcessId,
HANDLE CreatorProcessId,
PLARGE_INTEGER KnownParentCreateTime
) {
if (!Process || !ProcessId) return;
PROCESS_RELATION_ENTRY NewEntry = { 0 };
NewEntry.ProcessId = ProcessId;
NewEntry.ParentProcessId = ParentProcessId;
NewEntry.CreatorProcessId = CreatorProcessId;
NewEntry.CreateTime.QuadPart = PsGetProcessCreateTimeQuadPart(Process);
if (KnownParentCreateTime) {
NewEntry.ParentCreateTime = *KnownParentCreateTime;
}
else if (ParentProcessId) {
QueryProcessCreateTime(ParentProcessId, &NewEntry.ParentCreateTime);
}
NewEntry.Sequence = (ULONGLONG)InterlockedIncrement64(&g_ProcessRelationSequence);
ULONG Bucket = GetProcessRelationBucket(ProcessId);
ULONG Selected = 0;
KIRQL OldIrql;
KeAcquireSpinLock(&g_ProcessRelationLock, &OldIrql);
for (ULONG Index = 0; Index < PROCESS_RELATION_BUCKET_WAYS; Index++) {
PPROCESS_RELATION_ENTRY Entry = &g_ProcessRelations[Bucket][Index];
if (Entry->ProcessId == ProcessId || Entry->ProcessId == NULL) {
Selected = Index;
break;
}
if (Entry->Sequence < g_ProcessRelations[Bucket][Selected].Sequence) {
Selected = Index;
}
}
g_ProcessRelations[Bucket][Selected] = NewEntry;
KeReleaseSpinLock(&g_ProcessRelationLock, OldIrql);
}
static VOID RemoveProcessRelation(HANDLE ProcessId) {
if (!ProcessId) return;
ULONG Bucket = GetProcessRelationBucket(ProcessId);
KIRQL OldIrql;
KeAcquireSpinLock(&g_ProcessRelationLock, &OldIrql);
for (ULONG Index = 0; Index < PROCESS_RELATION_BUCKET_WAYS; Index++) {
PPROCESS_RELATION_ENTRY Entry = &g_ProcessRelations[Bucket][Index];
if (Entry->ProcessId == ProcessId) {
RtlZeroMemory(Entry, sizeof(*Entry));
break;
}
}
KeReleaseSpinLock(&g_ProcessRelationLock, OldIrql);
}
BOOLEAN GetProcessRelation(
HANDLE ProcessId,
PHANDLE ParentProcessId,
PHANDLE CreatorProcessId
) {
if (ParentProcessId) *ParentProcessId = NULL;
if (CreatorProcessId) *CreatorProcessId = NULL;
if (!ProcessId || !g_ProcessProtectionInitialized || KeGetCurrentIrql() > APC_LEVEL) return FALSE;
PEPROCESS Process = NULL;
NTSTATUS Status = PsLookupProcessByProcessId(ProcessId, &Process);
if (!NT_SUCCESS(Status)) return FALSE;
LARGE_INTEGER CurrentCreateTime = { 0 };
CurrentCreateTime.QuadPart = PsGetProcessCreateTimeQuadPart(Process);
PROCESS_RELATION_ENTRY Snapshot = { 0 };
ULONG Bucket = GetProcessRelationBucket(ProcessId);
KIRQL OldIrql;
KeAcquireSpinLock(&g_ProcessRelationLock, &OldIrql);
for (ULONG Index = 0; Index < PROCESS_RELATION_BUCKET_WAYS; Index++) {
PPROCESS_RELATION_ENTRY Entry = &g_ProcessRelations[Bucket][Index];
if (Entry->ProcessId == ProcessId) {
Snapshot = *Entry;
break;
}
}
KeReleaseSpinLock(&g_ProcessRelationLock, OldIrql);
BOOLEAN CacheValid = Snapshot.ProcessId == ProcessId &&
Snapshot.CreateTime.QuadPart == CurrentCreateTime.QuadPart;
if (CacheValid && Snapshot.ParentProcessId && Snapshot.ParentCreateTime.QuadPart != 0) {
LARGE_INTEGER LiveParentCreateTime = { 0 };
if (QueryProcessCreateTime(Snapshot.ParentProcessId, &LiveParentCreateTime) &&
LiveParentCreateTime.QuadPart != Snapshot.ParentCreateTime.QuadPart) {
CacheValid = FALSE;
}
}
if (CacheValid) {
if (ParentProcessId) *ParentProcessId = Snapshot.ParentProcessId;
if (CreatorProcessId) *CreatorProcessId = Snapshot.CreatorProcessId;
ObDereferenceObject(Process);
return Snapshot.ParentProcessId != NULL || Snapshot.CreatorProcessId != NULL;
}
if (Snapshot.ProcessId == ProcessId) {
RemoveProcessRelation(ProcessId);
}
HANDLE LiveParentProcessId = NULL;
LARGE_INTEGER LiveParentCreateTime = { 0 };
if (g_GetInheritedProcessId) {
LiveParentProcessId = g_GetInheritedProcessId(Process);
}
if (!LiveParentProcessId || LiveParentProcessId == ProcessId) {
ObDereferenceObject(Process);
return FALSE;
}
if (!QueryProcessCreateTime(LiveParentProcessId, &LiveParentCreateTime) ||
LiveParentCreateTime.QuadPart > CurrentCreateTime.QuadPart) {
ObDereferenceObject(Process);
return FALSE;
}
StoreProcessRelation(
Process,
ProcessId,
LiveParentProcessId,
NULL,
&LiveParentCreateTime
);
if (ParentProcessId) *ParentProcessId = LiveParentProcessId;
if (CreatorProcessId) *CreatorProcessId = NULL;
ObDereferenceObject(Process);
return TRUE;
}
NTSTATUS InitializeProcessProtection() {
if (g_ProcessProtectionInitialized) return STATUS_SUCCESS;
KeInitializeSpinLock(&g_ProcessRelationLock);
RtlZeroMemory(g_ProcessRelations, sizeof(g_ProcessRelations));
InterlockedExchange64(&g_ProcessRelationSequence, 0);
ExInitializeRundownProtection(&g_ProcessRundown);
UNICODE_STRING RoutineName = RTL_CONSTANT_STRING(L"PsGetProcessInheritedFromUniqueProcessId");
g_GetInheritedProcessId = reinterpret_cast<PPS_GET_PROCESS_INHERITED_FROM_UNIQUE_PROCESS_ID>(
MmGetSystemRoutineAddress(&RoutineName)
);
InterlockedExchange(&g_ProcessStopping, 0);
g_ProcessProtectionInitialized = TRUE;
return STATUS_SUCCESS;
}
NTSTATUS UninitializeProcessProtection() {
if (!g_ProcessProtectionInitialized) return STATUS_SUCCESS;
InterlockedExchange(&g_ProcessStopping, 1);
ExWaitForRundownProtectionRelease(&g_ProcessRundown);
KIRQL OldIrql;
KeAcquireSpinLock(&g_ProcessRelationLock, &OldIrql);
RtlZeroMemory(g_ProcessRelations, sizeof(g_ProcessRelations));
KeReleaseSpinLock(&g_ProcessRelationLock, OldIrql);
g_GetInheritedProcessId = NULL;
g_ProcessProtectionInitialized = FALSE;
return STATUS_SUCCESS;
}
VOID ProcessNotifyCallbackEx(
PEPROCESS Process,
HANDLE ProcessId,
PPS_CREATE_NOTIFY_INFO CreateInfo
) {
if (!g_ProcessProtectionInitialized) return;
if (InterlockedCompareExchange(&g_ProcessStopping, 0, 0) != 0) return;
if (!ExAcquireRundownProtection(&g_ProcessRundown)) return;
if (!CreateInfo) {
RemoveProcessRelation(ProcessId);
ExReleaseRundownProtection(&g_ProcessRundown);
return;
}
if (!NT_SUCCESS(CreateInfo->CreationStatus)) {
ExReleaseRundownProtection(&g_ProcessRundown);
return;
}
HANDLE CreatorPid = CreateInfo->CreatingThreadId.UniqueProcess;
HANDLE ParentPid = CreateInfo->ParentProcessId;
ULONG RuleCode = 0;
BOOLEAN Kill = FALSE;
BOOLEAN Blocked = EvaluateProcessCreateRule(
CreatorPid,
ParentPid,
ProcessId,
CreateInfo->ImageFileName,
CreateInfo->CommandLine,
CreateInfo->FileOpenNameAvailable ? TRUE : FALSE,
CreateInfo->IsSubsystemProcess ? TRUE : FALSE,
&RuleCode,
&Kill
);
if (Blocked) {
CreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
if (CreateInfo->ImageFileName && CreateInfo->ImageFileName->Buffer) {
SendMessageToUser(
RuleCode,
(ULONG)(ULONG_PTR)CreatorPid,
CreateInfo->ImageFileName->Buffer,
CreateInfo->ImageFileName->Length
);
}
QueueRuleProcessTermination(CreatorPid, Kill);
}
else {
StoreProcessRelation(Process, ProcessId, ParentPid, CreatorPid, NULL);
}
ExReleaseRundownProtection(&g_ProcessRundown);
}