From 11da9eb3bdcb80767993bf7adf63def6d53b20ba Mon Sep 17 00:00:00 2001 From: 87owo <85057800+87owo@users.noreply.github.com> Date: Wed, 1 Jul 2026 14:13:11 +0800 Subject: [PATCH] Add files via upload --- Plugins/Filter/DriverCommon.h | 50 +- Plugins/Filter/DriverEntry.cpp | 37 +- Plugins/Filter/ProtectBoot.cpp | 6 +- Plugins/Filter/ProtectFile.cpp | 295 +++------ Plugins/Filter/ProtectProcess.cpp | 272 +------- Plugins/Filter/ProtectRegistry.cpp | 140 ++--- Plugins/Filter/ProtectRules.cpp | 954 +++++++++++++---------------- 7 files changed, 641 insertions(+), 1113 deletions(-) diff --git a/Plugins/Filter/DriverCommon.h b/Plugins/Filter/DriverCommon.h index 4aa0e82..1070613 100644 --- a/Plugins/Filter/DriverCommon.h +++ b/Plugins/Filter/DriverCommon.h @@ -92,7 +92,6 @@ typedef struct _FS_BPIO_OUTPUT { ULONGLONG Reserved2; NTSTATUS Status; } FS_BPIO_OUTPUT, * PFS_BPIO_OUTPUT; - #endif typedef struct _PYAS_MESSAGE { @@ -126,6 +125,41 @@ typedef struct _RULE_NODE { UNICODE_STRING Pattern; } RULE_NODE, * PRULE_NODE; +typedef enum _RULE_CATEGORY { + RuleCategoryProcess, + RuleCategoryFile, + RuleCategoryRegistry, + RuleCategoryDevice, + RuleCategoryUnknown +} RULE_CATEGORY; + +#define OP_WRITE 0x01 +#define OP_DELETE 0x02 +#define OP_CREATE 0x04 +#define OP_EXECUTE 0x08 +#define OP_RENAME 0x10 +#define OP_IOCTL 0x20 + +BOOLEAN EvaluateDeviceRule(HANDLE ProcessId, PULONG OutCode); + +typedef struct _DYNAMIC_RULE { + ULONG Code; + RULE_CATEGORY Category; + ULONG Operations; + + PRULE_NODE Initiator; + PRULE_NODE InitiatorExclude; + PRULE_NODE Target; + PRULE_NODE TargetExclude; + PRULE_NODE CommandLine; + PRULE_NODE Extensions; + + ULONG Threshold; + ULONG TimeWindow; + + struct _DYNAMIC_RULE* Next; +} DYNAMIC_RULE, * PDYNAMIC_RULE; + extern DRIVER_DATA GlobalData; FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext); @@ -135,8 +169,6 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data, FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext); FLT_PREOP_CALLBACK_STATUS ProtectBoot_PreDeviceControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext); -NTSTATUS InitializeProcessProtection(); -VOID UninitializeProcessProtection(); NTSTATUS InitializeRegistryProtection(PDRIVER_OBJECT DriverObject); VOID UninitializeRegistryProtection(); @@ -149,21 +181,21 @@ VOID UninitializeRulesEngine(); VOID AddDynamicWhitelist(PUNICODE_STRING RuleStr); VOID RemoveDynamicWhitelist(PUNICODE_STRING RuleStr); -VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo); +NTSTATUS LoadRuleFile(PCUNICODE_STRING FilePath); +VOID ClearDynamicRules(); +VOID ProcessNotifyCallbackEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo); NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName); ULONG SafeGetPyasPid(); VOID SafeSetPyasPid(ULONG Pid); BOOLEAN IsProcessTrusted(HANDLE ProcessId); -BOOLEAN IsTargetProtected(HANDLE ProcessId); BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes); -BOOLEAN CheckRegistryRule(PCUNICODE_STRING KeyName); -BOOLEAN CheckFileExtensionRule(PCUNICODE_STRING FileName); -BOOLEAN CheckProtectedPathRule(PCUNICODE_STRING FileName); -BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID WriteBuffer, ULONG WriteLength, BOOLEAN IsWrite); +BOOLEAN EvaluateProcessRule(HANDLE ProcessId, PCUNICODE_STRING TargetPath, PCUNICODE_STRING CommandLine, PULONG OutCode); +BOOLEAN EvaluateFileRule(HANDLE ProcessId, PCUNICODE_STRING TargetPath, ULONG Operation, PVOID WriteBuffer, ULONG WriteLength, PULONG OutCode); +BOOLEAN EvaluateRegistryRule(HANDLE ProcessId, PCUNICODE_STRING KeyName, ULONG Operation, PULONG OutCode); NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize); diff --git a/Plugins/Filter/DriverEntry.cpp b/Plugins/Filter/DriverEntry.cpp index 7b6cf23..e674200 100644 --- a/Plugins/Filter/DriverEntry.cpp +++ b/Plugins/Filter/DriverEntry.cpp @@ -1,7 +1,7 @@ #include "DriverCommon.h" DRIVER_DATA GlobalData; -static BOOLEAN g_ImageNotifyRegistered = FALSE; +static BOOLEAN g_ProcessNotifyRegistered = FALSE; static NTSTATUS InstanceSetup(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_SETUP_FLAGS Flags, DEVICE_TYPE VolumeDeviceType, FLT_FILESYSTEM_TYPE VolumeFilesystemType) { UNREFERENCED_PARAMETER(FltObjects); @@ -40,14 +40,12 @@ static NTSTATUS DriverUnload(FLT_FILTER_UNLOAD_FLAGS Flags) { GlobalData.FilterHandle = NULL; } - if (g_ImageNotifyRegistered) { - PsRemoveLoadImageNotifyRoutine(ImageLoadNotify); - g_ImageNotifyRegistered = FALSE; + if (g_ProcessNotifyRegistered) { + PsSetCreateProcessNotifyRoutineEx(ProcessNotifyCallbackEx, TRUE); + g_ProcessNotifyRegistered = FALSE; } UninitializeRegistryProtection(); - UninitializeProcessProtection(); - UnloadRules(); UninitializeRulesEngine(); @@ -63,17 +61,24 @@ static NTSTATUS PortMessage(PVOID PortCookie, PVOID InputBuffer, ULONG InputBuff if (InputBuffer && InputBufferLength >= sizeof(PYAS_USER_MESSAGE)) { PPYAS_USER_MESSAGE msg = (PPYAS_USER_MESSAGE)InputBuffer; - if (msg->Command == 1 || msg->Command == 2) { + if (msg->Command >= 1 && msg->Command <= 4) { msg->Path[MAX_PATH_LEN - 1] = L'\0'; UNICODE_STRING us; RtlInitUnicodeString(&us, msg->Path); + if (us.Length > 0) { if (msg->Command == 1) { AddDynamicWhitelist(&us); } - else { + else if (msg->Command == 2) { RemoveDynamicWhitelist(&us); } + else if (msg->Command == 3) { + LoadRuleFile(&us); + } + } + if (msg->Command == 4) { + ClearDynamicRules(); } } } @@ -159,7 +164,6 @@ extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Reg GlobalData.Initialized = TRUE; ExInitializeRundownProtection(&GlobalData.PortRundown); - InitializeRulesEngine(); status = LoadRulesFromDisk(RegistryPath); @@ -187,24 +191,21 @@ extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Reg return status; } - InitializeProcessProtection(); - InitializeRegistryProtection(DriverObject); - NTSTATUS imageNotifyStatus = PsSetLoadImageNotifyRoutine(ImageLoadNotify); - if (NT_SUCCESS(imageNotifyStatus)) { - g_ImageNotifyRegistered = TRUE; + NTSTATUS processNotifyStatus = PsSetCreateProcessNotifyRoutineEx(ProcessNotifyCallbackEx, FALSE); + if (NT_SUCCESS(processNotifyStatus)) { + g_ProcessNotifyRegistered = TRUE; } status = FltStartFiltering(GlobalData.FilterHandle); if (!NT_SUCCESS(status)) { - if (g_ImageNotifyRegistered) { - PsRemoveLoadImageNotifyRoutine(ImageLoadNotify); - g_ImageNotifyRegistered = FALSE; + if (g_ProcessNotifyRegistered) { + PsSetCreateProcessNotifyRoutineEx(ProcessNotifyCallbackEx, TRUE); + g_ProcessNotifyRegistered = FALSE; } UninitializeRegistryProtection(); - UninitializeProcessProtection(); if (GlobalData.ServerPort) { FltCloseCommunicationPort(GlobalData.ServerPort); diff --git a/Plugins/Filter/ProtectBoot.cpp b/Plugins/Filter/ProtectBoot.cpp index 736ed20..7980061 100644 --- a/Plugins/Filter/ProtectBoot.cpp +++ b/Plugins/Filter/ProtectBoot.cpp @@ -22,9 +22,11 @@ FLT_PREOP_CALLBACK_STATUS ProtectBoot_PreDeviceControl(PFLT_CALLBACK_DATA Data, IoControlCode == IOCTL_DISK_FORMAT_TRACKS_EX) { HANDLE Pid = PsGetCurrentProcessId(); - if (!IsProcessTrusted(Pid)) { + ULONG RuleCode = 0; + + if (EvaluateDeviceRule(Pid, &RuleCode)) { UNICODE_STRING MsgStr = RTL_CONSTANT_STRING(L"Disk_Wiper_Attempt"); - SendMessageToUser(4001, (ULONG)(ULONG_PTR)Pid, MsgStr.Buffer, MsgStr.Length); + SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, MsgStr.Buffer, MsgStr.Length); Data->IoStatus.Status = STATUS_ACCESS_DENIED; return FLT_PREOP_COMPLETE; } diff --git a/Plugins/Filter/ProtectFile.cpp b/Plugins/Filter/ProtectFile.cpp index a86164c..ed27ec4 100644 --- a/Plugins/Filter/ProtectFile.cpp +++ b/Plugins/Filter/ProtectFile.cpp @@ -1,14 +1,5 @@ #include "DriverCommon.h" -static BOOLEAN IsHoneyToken(PUNICODE_STRING Path) { - if (!Path || !Path->Buffer) return FALSE; - if (WildcardMatch(L"*PYAS_Honey*", Path->Buffer, Path->Length) || - WildcardMatch(L"*Backup_Secret*", Path->Buffer, Path->Length)) { - return TRUE; - } - return FALSE; -} - FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) { UNREFERENCED_PARAMETER(FltObjects); UNREFERENCED_PARAMETER(CompletionContext); @@ -17,93 +8,37 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_R if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK; PFLT_FILE_NAME_INFORMATION nameInfo = NULL; - UNICODE_STRING FileName = { 0 }; - NTSTATUS status = STATUS_SUCCESS; - FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; - - status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo); - if (!NT_SUCCESS(status) || !nameInfo) { - return FLT_PREOP_SUCCESS_NO_CALLBACK; - } + NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo); + if (!NT_SUCCESS(status) || !nameInfo) return FLT_PREOP_SUCCESS_NO_CALLBACK; status = FltParseFileNameInformation(nameInfo); - if (!NT_SUCCESS(status)) { - FltReleaseFileNameInformation(nameInfo); - return FLT_PREOP_SUCCESS_NO_CALLBACK; - } - - FileName = nameInfo->Name; - if (!FileName.Buffer || FileName.Length == 0) { + if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer || nameInfo->Name.Length == 0) { FltReleaseFileNameInformation(nameInfo); return FLT_PREOP_SUCCESS_NO_CALLBACK; } HANDLE Pid = PsGetCurrentProcessId(); - BOOLEAN Trusted = IsProcessTrusted(Pid); + FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; ULONG CreateDisposition = (Data->Iopb->Parameters.Create.Options >> 24) & 0xFF; - BOOLEAN IsCreateAction = (CreateDisposition == FILE_CREATE || - CreateDisposition == FILE_SUPERSEDE || - CreateDisposition == FILE_OVERWRITE || - CreateDisposition == FILE_OVERWRITE_IF || - CreateDisposition == FILE_OPEN_IF); + BOOLEAN IsCreateAction = (CreateDisposition == FILE_CREATE || CreateDisposition == FILE_SUPERSEDE || CreateDisposition == FILE_OVERWRITE || CreateDisposition == FILE_OVERWRITE_IF || CreateDisposition == FILE_OPEN_IF); + ULONG DesiredAccess = Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess; - if (IsHoneyToken(&FileName)) { - if (!Trusted) { - if ((Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess & (FILE_WRITE_DATA | DELETE)) || IsCreateAction) { - SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, FileName.Buffer, FileName.Length); - Data->IoStatus.Status = STATUS_ACCESS_DENIED; - callbackStatus = FLT_PREOP_COMPLETE; - goto cleanup; - } + ULONG Operation = 0; + if (IsCreateAction) Operation |= OP_CREATE; + if (DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | WRITE_DAC | WRITE_OWNER | GENERIC_WRITE)) Operation |= OP_WRITE; + if (DesiredAccess & (DELETE | FILE_DELETE_CHILD)) Operation |= OP_DELETE; + + if (Operation != 0) { + ULONG RuleCode = 0; + if (EvaluateFileRule(Pid, &nameInfo->Name, Operation, NULL, 0, &RuleCode)) { + SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length); + Data->IoStatus.Status = STATUS_ACCESS_DENIED; + callbackStatus = FLT_PREOP_COMPLETE; } } - if (WildcardMatch(L"\\Device\\PhysicalDrive*", FileName.Buffer, FileName.Length) || - (WildcardMatch(L"\\Device\\Harddisk*", FileName.Buffer, FileName.Length) && !WildcardMatch(L"*Volume*", FileName.Buffer, FileName.Length))) { - - if (Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess & - (FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE | WRITE_DAC | WRITE_OWNER)) { - - if (!Trusted) { - UNICODE_STRING MsgStr = RTL_CONSTANT_STRING(L"Disk_Wiper_Attempt"); - SendMessageToUser(4001, (ULONG)(ULONG_PTR)Pid, MsgStr.Buffer, MsgStr.Length); - Data->IoStatus.Status = STATUS_ACCESS_DENIED; - callbackStatus = FLT_PREOP_COMPLETE; - goto cleanup; - } - } - } - - if (CheckProtectedPathRule(&FileName)) { - if (!Trusted) { - if ((Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess & - (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_DAC | GENERIC_WRITE)) || IsCreateAction) { - SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, FileName.Buffer, FileName.Length); - Data->IoStatus.Status = STATUS_ACCESS_DENIED; - callbackStatus = FLT_PREOP_COMPLETE; - goto cleanup; - } - } - } - - if (CheckFileExtensionRule(&FileName)) { - if (!Trusted) { - if (Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess & (DELETE | FILE_DELETE_CHILD)) { - if (CheckRansomActivity(Pid, &FileName, NULL, 0, FALSE)) { - SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, FileName.Buffer, FileName.Length); - Data->IoStatus.Status = STATUS_ACCESS_DENIED; - callbackStatus = FLT_PREOP_COMPLETE; - goto cleanup; - } - } - } - } - -cleanup: - if (nameInfo) { - FltReleaseFileNameInformation(nameInfo); - } + FltReleaseFileNameInformation(nameInfo); return callbackStatus; } @@ -115,178 +50,88 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreSetInfo(PFLT_CALLBACK_DATA Data, PCFLT_ if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK; FILE_INFORMATION_CLASS infoClass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass; - - if (infoClass != FileDispositionInformation && - infoClass != FileRenameInformation && - infoClass != FileRenameInformationEx) { + if (infoClass != FileDispositionInformation && infoClass != FileRenameInformation && infoClass != FileRenameInformationEx) { return FLT_PREOP_SUCCESS_NO_CALLBACK; } PFLT_FILE_NAME_INFORMATION nameInfo = NULL; - FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; - NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo); - - if (!NT_SUCCESS(status) || !nameInfo) goto cleanup; - - status = FltParseFileNameInformation(nameInfo); - if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) goto cleanup; + if (!NT_SUCCESS(FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo)) || !nameInfo) return FLT_PREOP_SUCCESS_NO_CALLBACK; + if (!NT_SUCCESS(FltParseFileNameInformation(nameInfo)) || !nameInfo->Name.Buffer) { + FltReleaseFileNameInformation(nameInfo); + return FLT_PREOP_SUCCESS_NO_CALLBACK; + } HANDLE Pid = PsGetCurrentProcessId(); - if (!IsProcessTrusted(Pid)) { - if (CheckProtectedPathRule(&nameInfo->Name)) { - SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length); - Data->IoStatus.Status = STATUS_ACCESS_DENIED; - callbackStatus = FLT_PREOP_COMPLETE; - goto cleanup; - } - - if (CheckFileExtensionRule(&nameInfo->Name)) { - if (CheckRansomActivity(Pid, &nameInfo->Name, NULL, 0, FALSE)) { - SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length); - Data->IoStatus.Status = STATUS_ACCESS_DENIED; - callbackStatus = FLT_PREOP_COMPLETE; - goto cleanup; - } - } - } - -cleanup: - if (nameInfo) { - FltReleaseFileNameInformation(nameInfo); - } - return callbackStatus; -} - -FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) { - UNREFERENCED_PARAMETER(FltObjects); - UNREFERENCED_PARAMETER(CompletionContext); - - if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK; - if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK; - - PFLT_FILE_NAME_INFORMATION nameInfo = NULL; FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; - NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo); + ULONG Operation = 0; - if (!NT_SUCCESS(status) || !nameInfo) goto cleanup; - - status = FltParseFileNameInformation(nameInfo); - if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) goto cleanup; - - SECURITY_INFORMATION SecurityInfo = Data->Iopb->Parameters.SetSecurity.SecurityInformation; - - if (SecurityInfo & (OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION)) { - if (CheckProtectedPathRule(&nameInfo->Name)) { - HANDLE Pid = PsGetCurrentProcessId(); - if (!IsProcessTrusted(Pid)) { - SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length); - Data->IoStatus.Status = STATUS_ACCESS_DENIED; - callbackStatus = FLT_PREOP_COMPLETE; - goto cleanup; - } - } + if (infoClass == FileDispositionInformation) { + Operation = OP_DELETE; + } + else { + Operation = OP_RENAME; } -cleanup: - if (nameInfo) { - FltReleaseFileNameInformation(nameInfo); + ULONG RuleCode = 0; + if (EvaluateFileRule(Pid, &nameInfo->Name, Operation, NULL, 0, &RuleCode)) { + SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length); + Data->IoStatus.Status = STATUS_ACCESS_DENIED; + callbackStatus = FLT_PREOP_COMPLETE; } + + FltReleaseFileNameInformation(nameInfo); return callbackStatus; } FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) { - UNREFERENCED_PARAMETER(CompletionContext); UNREFERENCED_PARAMETER(FltObjects); + UNREFERENCED_PARAMETER(CompletionContext); if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK; if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK; - - if (Data->Iopb->IrpFlags & (IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO | IRP_NOCACHE)) { - return FLT_PREOP_SUCCESS_NO_CALLBACK; - } + if (Data->Iopb->IrpFlags & (IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO | IRP_NOCACHE)) return FLT_PREOP_SUCCESS_NO_CALLBACK; ULONG WriteLength = Data->Iopb->Parameters.Write.Length; if (WriteLength == 0) return FLT_PREOP_SUCCESS_NO_CALLBACK; PFLT_FILE_NAME_INFORMATION nameInfo = NULL; - FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; - NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo); - - if (!NT_SUCCESS(status) || !nameInfo) goto cleanup; - - status = FltParseFileNameInformation(nameInfo); - if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) goto cleanup; - - if (CheckFileExtensionRule(&nameInfo->Name)) { - HANDLE Pid = PsGetCurrentProcessId(); - - if (!IsProcessTrusted(Pid)) { - PVOID SystemBuffer = NULL; - PMDL Mdl = Data->Iopb->Parameters.Write.MdlAddress; - - if (!Mdl) { - if (Data->Iopb->Parameters.Write.WriteBuffer && KeGetCurrentIrql() <= APC_LEVEL) { - if (NT_SUCCESS(FltLockUserBuffer(Data))) { - Mdl = Data->Iopb->Parameters.Write.MdlAddress; - } - } - } - - if (Mdl) { - SystemBuffer = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority | MdlMappingNoExecute); - } - - if (CheckRansomActivity(Pid, &nameInfo->Name, SystemBuffer, WriteLength, TRUE)) { - SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length); - Data->IoStatus.Status = STATUS_ACCESS_DENIED; - callbackStatus = FLT_PREOP_COMPLETE; - } - } - } - -cleanup: - if (nameInfo) { + if (!NT_SUCCESS(FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo)) || !nameInfo) return FLT_PREOP_SUCCESS_NO_CALLBACK; + if (!NT_SUCCESS(FltParseFileNameInformation(nameInfo)) || !nameInfo->Name.Buffer) { FltReleaseFileNameInformation(nameInfo); + return FLT_PREOP_SUCCESS_NO_CALLBACK; } + + HANDLE Pid = PsGetCurrentProcessId(); + FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; + + PVOID SystemBuffer = NULL; + PMDL Mdl = Data->Iopb->Parameters.Write.MdlAddress; + if (!Mdl && Data->Iopb->Parameters.Write.WriteBuffer && KeGetCurrentIrql() <= APC_LEVEL) { + if (NT_SUCCESS(FltLockUserBuffer(Data))) Mdl = Data->Iopb->Parameters.Write.MdlAddress; + } + if (Mdl) SystemBuffer = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority | MdlMappingNoExecute); + + ULONG RuleCode = 0; + if (EvaluateFileRule(Pid, &nameInfo->Name, OP_WRITE, SystemBuffer, WriteLength, &RuleCode)) { + SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length); + Data->IoStatus.Status = STATUS_ACCESS_DENIED; + callbackStatus = FLT_PREOP_COMPLETE; + } + + FltReleaseFileNameInformation(nameInfo); return callbackStatus; } -FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) { +FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) { + UNREFERENCED_PARAMETER(Data); UNREFERENCED_PARAMETER(FltObjects); UNREFERENCED_PARAMETER(CompletionContext); + return FLT_PREOP_SUCCESS_NO_CALLBACK; +} - if (Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode != FSCTL_MANAGE_BYPASS_IO) { - return FLT_PREOP_SUCCESS_NO_CALLBACK; - } - - PFS_BPIO_INPUT InputBuffer = (PFS_BPIO_INPUT)Data->Iopb->Parameters.FileSystemControl.Neither.InputBuffer; - if (!InputBuffer) return FLT_PREOP_SUCCESS_NO_CALLBACK; - - FS_BPIO_OPERATIONS Operation; - - __try { - Operation = InputBuffer->Operation; - } - __except (EXCEPTION_EXECUTE_HANDLER) { - return FLT_PREOP_SUCCESS_NO_CALLBACK; - } - - PFLT_FILE_NAME_INFORMATION nameInfo = NULL; - FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; - - if (Operation == FS_BPIO_OP_ENABLE) { - if (NT_SUCCESS(FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo)) && nameInfo) { - if (NT_SUCCESS(FltParseFileNameInformation(nameInfo))) { - if (CheckFileExtensionRule(&nameInfo->Name) || CheckProtectedPathRule(&nameInfo->Name)) { - Data->IoStatus.Status = STATUS_NOT_SUPPORTED; - callbackStatus = FLT_PREOP_COMPLETE; - } - } - } - } - - if (nameInfo) { - FltReleaseFileNameInformation(nameInfo); - } - return callbackStatus; +FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) { + UNREFERENCED_PARAMETER(Data); + UNREFERENCED_PARAMETER(FltObjects); + UNREFERENCED_PARAMETER(CompletionContext); + return FLT_PREOP_SUCCESS_NO_CALLBACK; } \ No newline at end of file diff --git a/Plugins/Filter/ProtectProcess.cpp b/Plugins/Filter/ProtectProcess.cpp index 99decf3..d3b630f 100644 --- a/Plugins/Filter/ProtectProcess.cpp +++ b/Plugins/Filter/ProtectProcess.cpp @@ -1,269 +1,21 @@ #include "DriverCommon.h" -static PVOID ObRegistrationHandle = NULL; -static OB_CALLBACK_REGISTRATION ObRegistration = {0}; -static OB_OPERATION_REGISTRATION ObCallbacks[2] = {0}; -static KSPIN_LOCK ObDataLock = {0}; -static BOOLEAN ObDataInitialized = FALSE; +VOID ProcessNotifyCallbackEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo) { + UNREFERENCED_PARAMETER(Process); + UNREFERENCED_PARAMETER(ProcessId); -static BOOLEAN IsSystemImage(PUNICODE_STRING FullImageName) { - if (!FullImageName || !FullImageName->Buffer) return FALSE; + if (CreateInfo != NULL) { + if (KeGetCurrentIrql() > APC_LEVEL) return; - if (WildcardMatch(L"*\\Windows\\System32\\*", FullImageName->Buffer, FullImageName->Length) || - WildcardMatch(L"*\\Windows\\SysWOW64\\*", FullImageName->Buffer, FullImageName->Length) || - WildcardMatch(L"*\\Windows\\WinSxS\\*", FullImageName->Buffer, FullImageName->Length) || - WildcardMatch(L"*\\Windows\\Microsoft.NET\\*", FullImageName->Buffer, FullImageName->Length) || - WildcardMatch(L"*\\Common Files\\Microsoft Shared\\*", FullImageName->Buffer, FullImageName->Length) || - WildcardMatch(L"*\\Program Files*\\*", FullImageName->Buffer, FullImageName->Length)) { - return TRUE; - } - return FALSE; -} + HANDLE ParentId = CreateInfo->CreatingThreadId.UniqueProcess; + if (!ParentId) return; -VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo) { - UNREFERENCED_PARAMETER(ImageInfo); - - if (KeGetCurrentIrql() > APC_LEVEL) return; - if (!FullImageName || !FullImageName->Buffer) return; - if (ProcessId == (HANDLE)0) return; - - if (IsSystemImage(FullImageName)) return; - - if (IsTargetProtected(ProcessId)) { - if (CheckFileExtensionRule(FullImageName)) { - SendMessageToUser(6001, (ULONG)(ULONG_PTR)ProcessId, FullImageName->Buffer, FullImageName->Length); - } - } -} - -static BOOLEAN IsCriticalSystemProcess(HANDLE ProcessId) { - if (ProcessId == (HANDLE)4) return TRUE; - - if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE; - - NTSTATUS status; - BOOLEAN isCritical = FALSE; - PEPROCESS Process = NULL; - PUNICODE_STRING imageFileName = NULL; - - status = PsLookupProcessByProcessId(ProcessId, &Process); - if (!NT_SUCCESS(status)) return FALSE; - - status = SeLocateProcessImageName(Process, &imageFileName); - - if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) { - if (WildcardMatch(L"*\\Windows\\System32\\csrss.exe", imageFileName->Buffer, imageFileName->Length) || - WildcardMatch(L"*\\Windows\\System32\\lsass.exe", imageFileName->Buffer, imageFileName->Length) || - WildcardMatch(L"*\\Windows\\System32\\services.exe", imageFileName->Buffer, imageFileName->Length) || - WildcardMatch(L"*\\Windows\\System32\\wininit.exe", imageFileName->Buffer, imageFileName->Length) || - WildcardMatch(L"*\\Windows\\System32\\smss.exe", imageFileName->Buffer, imageFileName->Length)) { - isCritical = TRUE; - } - } - - if (imageFileName) { - if (imageFileName->Buffer) ExFreePool(imageFileName->Buffer); - ExFreePool(imageFileName); - } - - ObDereferenceObject(Process); - return isCritical; -} - -static BOOLEAN IsBlacklistedAdminTool(HANDLE ProcessId) { - if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE; - - NTSTATUS status; - BOOLEAN isBlacklisted = FALSE; - PEPROCESS Process = NULL; - PUNICODE_STRING imageFileName = NULL; - - status = PsLookupProcessByProcessId(ProcessId, &Process); - if (!NT_SUCCESS(status)) return FALSE; - - status = SeLocateProcessImageName(Process, &imageFileName); - - if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) { - if (WildcardMatch(L"*Taskmgr.exe", imageFileName->Buffer, imageFileName->Length) || - WildcardMatch(L"*taskkill.exe", imageFileName->Buffer, imageFileName->Length) || - WildcardMatch(L"*ProcessHacker.exe", imageFileName->Buffer, imageFileName->Length) || - WildcardMatch(L"*procmon.exe", imageFileName->Buffer, imageFileName->Length)) { - isBlacklisted = TRUE; - } - } - - if (imageFileName) { - if (imageFileName->Buffer) ExFreePool(imageFileName->Buffer); - ExFreePool(imageFileName); - } - - ObDereferenceObject(Process); - return isBlacklisted; -} - -static OB_PREOP_CALLBACK_STATUS PreOpenProcess(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation) { - UNREFERENCED_PARAMETER(RegistrationContext); - - if (OperationInformation->KernelHandle) return OB_PREOP_SUCCESS; - - PEPROCESS TargetProcess = (PEPROCESS)OperationInformation->Object; - if (!TargetProcess) return OB_PREOP_SUCCESS; - - HANDLE TargetPid = PsGetProcessId(TargetProcess); - HANDLE SourcePid = PsGetCurrentProcessId(); - - if (SourcePid == TargetPid) return OB_PREOP_SUCCESS; - if ((ULONG)(ULONG_PTR)SourcePid == SafeGetPyasPid()) return OB_PREOP_SUCCESS; - if (SourcePid == (HANDLE)4) return OB_PREOP_SUCCESS; - - if (IsTargetProtected(TargetPid)) { - if (IsCriticalSystemProcess(SourcePid)) return OB_PREOP_SUCCESS; - - BOOLEAN isBlacklisted = FALSE; - if (KeGetCurrentIrql() == PASSIVE_LEVEL) { - isBlacklisted = IsBlacklistedAdminTool(SourcePid); - } - - if (isBlacklisted) { - ACCESS_MASK DenyMask = PROCESS_TERMINATE | - PROCESS_VM_OPERATION | - PROCESS_VM_WRITE | - PROCESS_CREATE_THREAD | - PROCESS_VM_READ | - PROCESS_DUP_HANDLE | - PROCESS_SUSPEND_RESUME | - PROCESS_SET_INFORMATION | - PROCESS_SET_QUOTA; - - OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask); - - if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) { - OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask); + ULONG RuleCode = 0; + if (EvaluateProcessRule(ParentId, CreateInfo->ImageFileName, CreateInfo->CommandLine, &RuleCode)) { + CreateInfo->CreationStatus = STATUS_ACCESS_DENIED; + if (CreateInfo->ImageFileName) { + SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)ParentId, CreateInfo->ImageFileName->Buffer, CreateInfo->ImageFileName->Length); } - return OB_PREOP_SUCCESS; } - - BOOLEAN bIsTrusted = IsProcessTrusted(SourcePid); - if (bIsTrusted) return OB_PREOP_SUCCESS; - - ACCESS_MASK DenyMask = PROCESS_TERMINATE | - PROCESS_VM_OPERATION | - PROCESS_VM_WRITE | - PROCESS_CREATE_THREAD | - PROCESS_VM_READ | - PROCESS_DUP_HANDLE | - PROCESS_SUSPEND_RESUME | - PROCESS_SET_INFORMATION | - PROCESS_SET_QUOTA; - - OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask); - - if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) { - OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask); - } - } - - return OB_PREOP_SUCCESS; -} - -static OB_PREOP_CALLBACK_STATUS PreOpenThread(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation) { - UNREFERENCED_PARAMETER(RegistrationContext); - - if (OperationInformation->KernelHandle) return OB_PREOP_SUCCESS; - - PETHREAD TargetThread = (PETHREAD)OperationInformation->Object; - if (!TargetThread) return OB_PREOP_SUCCESS; - - PEPROCESS TargetProcess = IoThreadToProcess(TargetThread); - if (!TargetProcess) return OB_PREOP_SUCCESS; - - HANDLE TargetPid = PsGetProcessId(TargetProcess); - HANDLE SourcePid = PsGetCurrentProcessId(); - - if (SourcePid == TargetPid) return OB_PREOP_SUCCESS; - if ((ULONG)(ULONG_PTR)SourcePid == SafeGetPyasPid()) return OB_PREOP_SUCCESS; - if (SourcePid == (HANDLE)4) return OB_PREOP_SUCCESS; - - if (IsTargetProtected(TargetPid)) { - if (IsCriticalSystemProcess(SourcePid)) return OB_PREOP_SUCCESS; - - BOOLEAN isBlacklisted = FALSE; - if (KeGetCurrentIrql() == PASSIVE_LEVEL) { - isBlacklisted = IsBlacklistedAdminTool(SourcePid); - } - - if (isBlacklisted) { - ACCESS_MASK DenyMask = THREAD_TERMINATE | - THREAD_SUSPEND_RESUME | - THREAD_SET_CONTEXT | - THREAD_SET_INFORMATION | - THREAD_SET_THREAD_TOKEN; - - OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask); - - if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) { - OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask); - } - return OB_PREOP_SUCCESS; - } - - BOOLEAN bIsTrusted = IsProcessTrusted(SourcePid); - if (bIsTrusted) return OB_PREOP_SUCCESS; - - ACCESS_MASK DenyMask = THREAD_TERMINATE | - THREAD_SUSPEND_RESUME | - THREAD_SET_CONTEXT | - THREAD_SET_INFORMATION | - THREAD_SET_THREAD_TOKEN; - - OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask); - - if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) { - OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask); - } - } - - return OB_PREOP_SUCCESS; -} - -NTSTATUS InitializeProcessProtection() { - static UNICODE_STRING Altitude = RTL_CONSTANT_STRING(L"320000.PYAS.Ob"); - - KeInitializeSpinLock(&ObDataLock); - ObDataInitialized = TRUE; - - ObCallbacks[0].ObjectType = PsProcessType; - ObCallbacks[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE; - ObCallbacks[0].PreOperation = PreOpenProcess; - ObCallbacks[0].PostOperation = NULL; - - ObCallbacks[1].ObjectType = PsThreadType; - ObCallbacks[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE; - ObCallbacks[1].PreOperation = PreOpenThread; - ObCallbacks[1].PostOperation = NULL; - - ObRegistration.Version = OB_FLT_REGISTRATION_VERSION; - ObRegistration.OperationRegistrationCount = 2; - ObRegistration.Altitude = Altitude; - ObRegistration.RegistrationContext = NULL; - ObRegistration.OperationRegistration = ObCallbacks; - - NTSTATUS status = ObRegisterCallbacks(&ObRegistration, &ObRegistrationHandle); - - if (!NT_SUCCESS(status) && (status == STATUS_FLT_INSTANCE_ALTITUDE_COLLISION || status == STATUS_OBJECT_NAME_COLLISION)) { - static UNICODE_STRING FallbackAltitude = RTL_CONSTANT_STRING(L"320000.PYAS.Ob.Fallback"); - ObRegistration.Altitude = FallbackAltitude; - status = ObRegisterCallbacks(&ObRegistration, &ObRegistrationHandle); - } - - return status; -} - -VOID UninitializeProcessProtection() { - ObDataInitialized = FALSE; - - if (ObRegistrationHandle) { - ObUnRegisterCallbacks(ObRegistrationHandle); - ObRegistrationHandle = NULL; } } \ No newline at end of file diff --git a/Plugins/Filter/ProtectRegistry.cpp b/Plugins/Filter/ProtectRegistry.cpp index f780700..bfec822 100644 --- a/Plugins/Filter/ProtectRegistry.cpp +++ b/Plugins/Filter/ProtectRegistry.cpp @@ -2,14 +2,6 @@ static LARGE_INTEGER Cookie; -static BOOLEAN IsBamRegistryPath(PCUNICODE_STRING KeyName) { - if (!KeyName || !KeyName->Buffer) return FALSE; - if (WildcardMatch(L"*\\Services\\bam\\*", KeyName->Buffer, KeyName->Length)) { - return TRUE; - } - return FALSE; -} - static BOOLEAN IsWriteAccess(ACCESS_MASK Access) { if (Access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK | DELETE | WRITE_DAC | WRITE_OWNER | GENERIC_WRITE)) { @@ -106,12 +98,11 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A UNICODE_STRING FullPath; RtlInitUnicodeString(&FullPath, FullPathBuffer); - if (CheckRegistryRule(&FullPath)) { - HANDLE Pid = PsGetCurrentProcessId(); - if (!IsProcessTrusted(Pid)) { - SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length); - status = STATUS_ACCESS_DENIED; - } + HANDLE Pid = PsGetCurrentProcessId(); + ULONG RuleCode = 0; + if (EvaluateRegistryRule(Pid, &FullPath, OP_CREATE | OP_WRITE, &RuleCode)) { + SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length); + status = STATUS_ACCESS_DENIED; } PyasFree(FullPathBuffer); } @@ -124,12 +115,11 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) { if (KeyName) { - if (CheckRegistryRule(KeyName)) { - HANDLE Pid = PsGetCurrentProcessId(); - if (!IsProcessTrusted(Pid)) { - SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, KeyName->Buffer, KeyName->Length); - status = STATUS_ACCESS_DENIED; - } + HANDLE Pid = PsGetCurrentProcessId(); + ULONG RuleCode = 0; + if (EvaluateRegistryRule(Pid, KeyName, OP_DELETE, &RuleCode)) { + SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, KeyName->Buffer, KeyName->Length); + status = STATUS_ACCESS_DENIED; } CmCallbackReleaseKeyObjectIDEx(KeyName); } @@ -142,44 +132,39 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A PCUNICODE_STRING KeyName = NULL; if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) { - if (KeyName) { - if (KeyName->Buffer) { - if (!IsBamRegistryPath(KeyName)) { - ULONG FullSize = KeyName->Length + sizeof(WCHAR) + - (Info->ValueName ? Info->ValueName->Length : 0) + - sizeof(WCHAR); + if (KeyName && KeyName->Buffer) { + ULONG FullSize = KeyName->Length + sizeof(WCHAR) + + (Info->ValueName ? Info->ValueName->Length : 0) + + sizeof(WCHAR); - if (FullSize <= 0xFFFF) { - PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize); - if (Buffer) { - UNICODE_STRING FullPath; - FullPath.Buffer = Buffer; - FullPath.Length = 0; - FullPath.MaximumLength = (USHORT)FullSize; + if (FullSize <= 0xFFFF) { + PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize); + if (Buffer) { + UNICODE_STRING FullPath; + FullPath.Buffer = Buffer; + FullPath.Length = 0; + FullPath.MaximumLength = (USHORT)FullSize; - RtlCopyUnicodeString(&FullPath, KeyName); + RtlCopyUnicodeString(&FullPath, KeyName); - if (Info->ValueName && Info->ValueName->Length > 0) { - if (FullPath.Length > 0 && FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\') { - RtlAppendUnicodeToString(&FullPath, L"\\"); - } - RtlAppendUnicodeStringToString(&FullPath, Info->ValueName); - } - - if (CheckRegistryRule(&FullPath)) { - HANDLE Pid = PsGetCurrentProcessId(); - if (!IsProcessTrusted(Pid)) { - SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length); - status = STATUS_ACCESS_DENIED; - } - } - PyasFree(Buffer); + if (Info->ValueName && Info->ValueName->Length > 0) { + if (FullPath.Length > 0 && FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\') { + RtlAppendUnicodeToString(&FullPath, L"\\"); } + RtlAppendUnicodeStringToString(&FullPath, Info->ValueName); } + + HANDLE Pid = PsGetCurrentProcessId(); + ULONG RuleCode = 0; + if (EvaluateRegistryRule(Pid, &FullPath, OP_WRITE, &RuleCode)) { + SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length); + status = STATUS_ACCESS_DENIED; + } + PyasFree(Buffer); } } - CmCallbackReleaseKeyObjectIDEx(KeyName); } + if (KeyName) CmCallbackReleaseKeyObjectIDEx(KeyName); } return status; } @@ -189,44 +174,39 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A PCUNICODE_STRING KeyName = NULL; if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) { - if (KeyName) { - if (KeyName->Buffer) { - if (!IsBamRegistryPath(KeyName)) { - ULONG FullSize = KeyName->Length + sizeof(WCHAR) + - (Info->ValueName ? Info->ValueName->Length : 0) + - sizeof(WCHAR); + if (KeyName && KeyName->Buffer) { + ULONG FullSize = KeyName->Length + sizeof(WCHAR) + + (Info->ValueName ? Info->ValueName->Length : 0) + + sizeof(WCHAR); - if (FullSize <= 0xFFFF) { - PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize); - if (Buffer) { - UNICODE_STRING FullPath; - FullPath.Buffer = Buffer; - FullPath.Length = 0; - FullPath.MaximumLength = (USHORT)FullSize; + if (FullSize <= 0xFFFF) { + PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize); + if (Buffer) { + UNICODE_STRING FullPath; + FullPath.Buffer = Buffer; + FullPath.Length = 0; + FullPath.MaximumLength = (USHORT)FullSize; - RtlCopyUnicodeString(&FullPath, KeyName); + RtlCopyUnicodeString(&FullPath, KeyName); - if (Info->ValueName && Info->ValueName->Length > 0) { - if (FullPath.Length > 0 && FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\') { - RtlAppendUnicodeToString(&FullPath, L"\\"); - } - RtlAppendUnicodeStringToString(&FullPath, Info->ValueName); - } - - if (CheckRegistryRule(&FullPath)) { - HANDLE Pid = PsGetCurrentProcessId(); - if (!IsProcessTrusted(Pid)) { - SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length); - status = STATUS_ACCESS_DENIED; - } - } - PyasFree(Buffer); + if (Info->ValueName && Info->ValueName->Length > 0) { + if (FullPath.Length > 0 && FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\') { + RtlAppendUnicodeToString(&FullPath, L"\\"); } + RtlAppendUnicodeStringToString(&FullPath, Info->ValueName); } + + HANDLE Pid = PsGetCurrentProcessId(); + ULONG RuleCode = 0; + if (EvaluateRegistryRule(Pid, &FullPath, OP_DELETE, &RuleCode)) { + SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length); + status = STATUS_ACCESS_DENIED; + } + PyasFree(Buffer); } } - CmCallbackReleaseKeyObjectIDEx(KeyName); } + if (KeyName) CmCallbackReleaseKeyObjectIDEx(KeyName); } return status; } diff --git a/Plugins/Filter/ProtectRules.cpp b/Plugins/Filter/ProtectRules.cpp index ddf3f82..8953a04 100644 --- a/Plugins/Filter/ProtectRules.cpp +++ b/Plugins/Filter/ProtectRules.cpp @@ -1,19 +1,17 @@ #include "DriverCommon.h" -constexpr auto MAX_TRACKERS = 64; -constexpr auto RANSOM_TIME_WINDOW_MS = 3000; -constexpr auto RANSOM_COUNT_THRESHOLD = 5; -constexpr auto HIGH_ENTROPY_THRESHOLD = 10; - +constexpr auto MAX_TRACKERS = 128; constexpr auto TRUST_CACHE_SIZE = 128; constexpr auto TRUST_CACHE_TTL_SEC = 300; +constexpr auto HIGH_ENTROPY_THRESHOLD = 10; -typedef struct _RANSOM_TRACKER { +typedef struct _BEHAVIOR_TRACKER { PEPROCESS Process; + PDYNAMIC_RULE Rule; LARGE_INTEGER ProcessCreateTime; ULONG ActivityCount; LARGE_INTEGER LastActivityTime; -} RANSOM_TRACKER, * PRANSOM_TRACKER; +} BEHAVIOR_TRACKER, * PBEHAVIOR_TRACKER; typedef struct _TRUST_CACHE_ENTRY { PEPROCESS Process; @@ -22,25 +20,19 @@ typedef struct _TRUST_CACHE_ENTRY { LARGE_INTEGER CacheTime; } TRUST_CACHE_ENTRY, * PTRUST_CACHE_ENTRY; -static RANSOM_TRACKER RansomTrackers[MAX_TRACKERS]; +static BEHAVIOR_TRACKER BehaviorTrackers[MAX_TRACKERS]; static TRUST_CACHE_ENTRY TrustCache[TRUST_CACHE_SIZE]; static KSPIN_LOCK TrustCacheLock; static BOOLEAN g_CacheInitialized = FALSE; static ERESOURCE RuleLock; -static PRULE_NODE g_RegistryBlockList = NULL; -static PRULE_NODE g_RegistryTrustedList = NULL; +static PDYNAMIC_RULE g_DynamicRules = NULL; static PRULE_NODE g_ProcessTrustedPaths = NULL; -static PRULE_NODE g_ProcessExploitable = NULL; -static PRULE_NODE g_FileProtectedPaths = NULL; -static PRULE_NODE g_FileExceptionPaths = NULL; -static PRULE_NODE g_FileRansomExts = NULL; + +static BOOLEAN CheckRuleThreshold(PDYNAMIC_RULE Rule, HANDLE ProcessId); ULONG SafeGetPyasPid() { - if (KeGetCurrentIrql() >= DISPATCH_LEVEL) { - return GlobalData.PyasPid; - } - + if (KeGetCurrentIrql() >= DISPATCH_LEVEL) return GlobalData.PyasPid; KIRQL OldIrql; KeAcquireSpinLock(&GlobalData.PidLock, &OldIrql); ULONG Pid = GlobalData.PyasPid; @@ -49,25 +41,13 @@ ULONG SafeGetPyasPid() { } VOID SafeSetPyasPid(ULONG Pid) { - if (KeGetCurrentIrql() >= DISPATCH_LEVEL) { - return; - } - + if (KeGetCurrentIrql() >= DISPATCH_LEVEL) return; KIRQL OldIrql; KeAcquireSpinLock(&GlobalData.PidLock, &OldIrql); GlobalData.PyasPid = Pid; KeReleaseSpinLock(&GlobalData.PidLock, OldIrql); } -const PCWSTR Helper_NaturallyCompressedExtensions[] = { - L".zip", L".7z", L".rar", L".tar", L".gz", - L".jpg", L".jpeg", L".png", L".webp", L".gif", - L".mp3", L".wav", L".aac", L".ogg", L".flac", - L".mp4", L".avi", L".mov", L".wmv", L".mkv", - L".docx", L".xlsx", L".pptx", L".pdf", L".wps", - L".apk", L".jar", L".class", L".db", L".sqlite" -}; - static VOID FreeList(PRULE_NODE* Head) { PRULE_NODE Current = *Head; while (Current) { @@ -81,59 +61,61 @@ static VOID FreeList(PRULE_NODE* Head) { static VOID AddRule(PRULE_NODE* Head, PUNICODE_STRING RuleStr) { if (!RuleStr || !RuleStr->Buffer) return; - PRULE_NODE Check = *Head; while (Check) { - if (RtlEqualUnicodeString(&Check->Pattern, RuleStr, TRUE)) { - return; - } + if (RtlEqualUnicodeString(&Check->Pattern, RuleStr, TRUE)) return; Check = Check->Next; } - PRULE_NODE Node = (PRULE_NODE)PyasAllocate(sizeof(RULE_NODE)); if (!Node) return; - SIZE_T Size = RuleStr->Length + sizeof(WCHAR); Node->Pattern.Buffer = (PWCHAR)PyasAllocate(Size); if (!Node->Pattern.Buffer) { PyasFree(Node); return; } - RtlCopyMemory(Node->Pattern.Buffer, RuleStr->Buffer, RuleStr->Length); Node->Pattern.Buffer[RuleStr->Length / sizeof(WCHAR)] = L'\0'; Node->Pattern.Length = RuleStr->Length; Node->Pattern.MaximumLength = (USHORT)Size; - Node->Next = *Head; *Head = Node; } +static VOID RemoveRule(PRULE_NODE* Head, PUNICODE_STRING RuleStr) { + if (!RuleStr || !RuleStr->Buffer) return; + PRULE_NODE Current = *Head; + PRULE_NODE Previous = NULL; + while (Current) { + if (RtlEqualUnicodeString(&Current->Pattern, RuleStr, TRUE)) { + PRULE_NODE ToDelete = Current; + if (Previous) Previous->Next = Current->Next; + else *Head = Current->Next; + Current = Current->Next; + if (ToDelete->Pattern.Buffer) PyasFree(ToDelete->Pattern.Buffer); + PyasFree(ToDelete); + } + else { + Previous = Current; + Current = Current->Next; + } + } +} + static BOOLEAN HasSuffix(PCUNICODE_STRING String, PCWSTR Suffix) { if (!String || !String->Buffer || !Suffix) return FALSE; - SIZE_T StringLenChars = String->Length / sizeof(WCHAR); - SIZE_T SuffixLenChars = 0; - - while (Suffix[SuffixLenChars] != L'\0') { - SuffixLenChars++; - } - + SIZE_T SuffixLenChars = wcslen(Suffix); if (StringLenChars < SuffixLenChars) return FALSE; - PCWSTR Ptr = String->Buffer + (StringLenChars - SuffixLenChars); - for (SIZE_T i = 0; i < SuffixLenChars; i++) { - if (RtlDowncaseUnicodeChar(Ptr[i]) != RtlDowncaseUnicodeChar(Suffix[i])) { - return FALSE; - } + if (RtlDowncaseUnicodeChar(Ptr[i]) != RtlDowncaseUnicodeChar(Suffix[i])) return FALSE; } return TRUE; } BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes) { if (Pattern == NULL || String == NULL) return FALSE; - USHORT StringLenChars = StringLengthBytes / sizeof(WCHAR); PCWSTR mp = NULL; PCWSTR cp = NULL; @@ -156,38 +138,46 @@ BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes) { return FALSE; } } - while (*Pattern == L'*') { - Pattern++; - } - + while (*Pattern == L'*') Pattern++; return (*Pattern == L'\0') ? TRUE : FALSE; } +static BOOLEAN MatchNodeListAny(PRULE_NODE Node, PCUNICODE_STRING Target) { + if (!Node) return FALSE; + if (!Target || !Target->Buffer) return FALSE; + while (Node) { + if (WildcardMatch(Node->Pattern.Buffer, Target->Buffer, Target->Length)) return TRUE; + Node = Node->Next; + } + return FALSE; +} + +static BOOLEAN MatchExtensionList(PRULE_NODE Node, PCUNICODE_STRING Target) { + if (!Node) return FALSE; + if (!Target || !Target->Buffer) return FALSE; + while (Node) { + if (HasSuffix(Target, Node->Pattern.Buffer)) return TRUE; + Node = Node->Next; + } + return FALSE; +} + static ULONG ProcessJsonUnescape(PWCHAR Buffer, ULONG LengthChars) { if (!Buffer || LengthChars == 0) return 0; - - ULONG WriteIdx = 0; - ULONG ReadIdx = 0; - + ULONG WriteIdx = 0, ReadIdx = 0; while (ReadIdx < LengthChars) { if (Buffer[ReadIdx] == L'\\' && (ReadIdx + 1 < LengthChars)) { WCHAR NextChar = Buffer[ReadIdx + 1]; if (NextChar == L'\\' || NextChar == L'"' || NextChar == L'/') { - Buffer[WriteIdx++] = NextChar; - ReadIdx += 2; + Buffer[WriteIdx++] = NextChar; ReadIdx += 2; } else if (NextChar == L'n') { Buffer[WriteIdx++] = L'\n'; ReadIdx += 2; } else if (NextChar == L'r') { Buffer[WriteIdx++] = L'\r'; ReadIdx += 2; } else if (NextChar == L't') { Buffer[WriteIdx++] = L'\t'; ReadIdx += 2; } - else { - Buffer[WriteIdx++] = Buffer[ReadIdx++]; - } - } - else { - Buffer[WriteIdx++] = Buffer[ReadIdx++]; + else { Buffer[WriteIdx++] = Buffer[ReadIdx++]; } } + else { Buffer[WriteIdx++] = Buffer[ReadIdx++]; } } - Buffer[WriteIdx] = L'\0'; return WriteIdx * sizeof(WCHAR); } @@ -195,101 +185,215 @@ static ULONG ProcessJsonUnescape(PWCHAR Buffer, ULONG LengthChars) { static VOID SkipWhitespace(PCHAR* Ptr, PCHAR End) { while (*Ptr < End) { char c = **Ptr; - if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { - (*Ptr)++; + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') (*Ptr)++; + else break; + } +} + +static ULONG ParseInt(PCHAR* Ptr, PCHAR End) { + ULONG val = 0; + SkipWhitespace(Ptr, End); + while (*Ptr < End && **Ptr >= '0' && **Ptr <= '9') { + val = val * 10 + (**Ptr - '0'); + (*Ptr)++; + } + return val; +} + +static VOID ParseStringArray(PCHAR* Ptr, PCHAR End, PRULE_NODE* Head) { + SkipWhitespace(Ptr, End); + if (*Ptr >= End || **Ptr != '[') return; + (*Ptr)++; + while (*Ptr < End) { + SkipWhitespace(Ptr, End); + if (*Ptr >= End || **Ptr == ']') { + if (*Ptr < End) (*Ptr)++; + return; + } + if (**Ptr == '"') { + PCHAR StartQuote = ++(*Ptr); + BOOLEAN Escaped = FALSE; + while (*Ptr < End) { + if (**Ptr == '"' && !Escaped) break; + Escaped = (**Ptr == '\\' && !Escaped); + (*Ptr)++; + } + if (*Ptr < End && **Ptr == '"') { + ULONG UTF8Len = (ULONG)(*Ptr - StartQuote); + if (UTF8Len > 0) { + ULONG WideSize = 0; + RtlUTF8ToUnicodeN(NULL, 0, &WideSize, StartQuote, UTF8Len); + if (WideSize > 0) { + PWCHAR WideBuffer = (PWCHAR)PyasAllocate(WideSize + sizeof(WCHAR)); + if (WideBuffer) { + ULONG ResultSize = 0; + RtlUTF8ToUnicodeN(WideBuffer, WideSize, &ResultSize, StartQuote, UTF8Len); + ULONG FinalSize = ProcessJsonUnescape(WideBuffer, ResultSize / sizeof(WCHAR)); + UNICODE_STRING Us; + Us.Buffer = WideBuffer; + Us.Length = (USHORT)FinalSize; + Us.MaximumLength = (USHORT)(WideSize + sizeof(WCHAR)); + AddRule(Head, &Us); + PyasFree(WideBuffer); + } + } + } + (*Ptr)++; + } } else { - break; + (*Ptr)++; } } } -static VOID ParseAndLoadRules(PCHAR JsonContent, ULONG ContentLength, PCSTR KeyName, PRULE_NODE* ListHead) { - if (!JsonContent || !KeyName || !ListHead) return; +static VOID ParseOperationsArray(PCHAR* Ptr, PCHAR End, PULONG Operations) { + PRULE_NODE TempHead = NULL; + ParseStringArray(Ptr, End, &TempHead); + PRULE_NODE Node = TempHead; + while (Node) { + UNICODE_STRING WriteStr = RTL_CONSTANT_STRING(L"Write"); + UNICODE_STRING DeleteStr = RTL_CONSTANT_STRING(L"Delete"); + UNICODE_STRING CreateStr = RTL_CONSTANT_STRING(L"Create"); + UNICODE_STRING ExecuteStr = RTL_CONSTANT_STRING(L"Execute"); + UNICODE_STRING RenameStr = RTL_CONSTANT_STRING(L"Rename"); + UNICODE_STRING IoctlStr = RTL_CONSTANT_STRING(L"Ioctl"); + if (RtlEqualUnicodeString(&Node->Pattern, &WriteStr, TRUE)) *Operations |= OP_WRITE; + else if (RtlEqualUnicodeString(&Node->Pattern, &DeleteStr, TRUE)) *Operations |= OP_DELETE; + else if (RtlEqualUnicodeString(&Node->Pattern, &CreateStr, TRUE)) *Operations |= OP_CREATE; + else if (RtlEqualUnicodeString(&Node->Pattern, &ExecuteStr, TRUE)) *Operations |= OP_EXECUTE; + else if (RtlEqualUnicodeString(&Node->Pattern, &RenameStr, TRUE)) *Operations |= OP_RENAME; + else if (RtlEqualUnicodeString(&Node->Pattern, &IoctlStr, TRUE)) *Operations |= OP_IOCTL; + + Node = Node->Next; + } + FreeList(&TempHead); +} + +static VOID ParseDynamicRules(PCHAR JsonContent, ULONG ContentLength) { PCHAR Ptr = JsonContent; PCHAR End = JsonContent + ContentLength; - SIZE_T KeyLen = 0; - while (KeyName[KeyLen] != '\0') KeyLen++; - while (Ptr < End) { - if (*Ptr == '"') { - if ((SIZE_T)(End - Ptr) > KeyLen && RtlCompareMemory(Ptr + 1, KeyName, KeyLen) == KeyLen) { - if (*(Ptr + 1 + KeyLen) == '"') { - Ptr += 1 + KeyLen + 1; + const char* TargetKey = "\"DynamicRules\""; + SIZE_T KeyLen = strlen(TargetKey); - SkipWhitespace(&Ptr, End); - if (Ptr >= End || *Ptr != ':') continue; + while (Ptr < End - KeyLen) { + if (RtlCompareMemory(Ptr, TargetKey, KeyLen) == KeyLen) { + Ptr += KeyLen; + SkipWhitespace(&Ptr, End); + if (Ptr >= End || *Ptr != ':') continue; + Ptr++; + SkipWhitespace(&Ptr, End); + if (Ptr >= End || *Ptr != '[') continue; + Ptr++; + + while (Ptr < End) { + SkipWhitespace(&Ptr, End); + if (Ptr >= End || *Ptr == ']') break; + if (*Ptr == '{') { Ptr++; - - SkipWhitespace(&Ptr, End); - if (Ptr >= End || *Ptr != '[') continue; - Ptr++; - - while (Ptr < End) { - SkipWhitespace(&Ptr, End); - if (Ptr >= End || *Ptr == ']') { - if (Ptr < End) Ptr++; - return; - } - - if (*Ptr == '"') { - PCHAR StartQuote = ++Ptr; - BOOLEAN Escaped = FALSE; - - while (Ptr < End) { - if (*Ptr == '"' && Escaped == FALSE) break; - if (*Ptr == '\\') { - Escaped = (Escaped == FALSE) ? TRUE : FALSE; - } - else { - Escaped = FALSE; - } - Ptr++; + PDYNAMIC_RULE Rule = (PDYNAMIC_RULE)PyasAllocate(sizeof(DYNAMIC_RULE)); + if (Rule) { + RtlZeroMemory(Rule, sizeof(DYNAMIC_RULE)); + while (Ptr < End) { + SkipWhitespace(&Ptr, End); + if (Ptr >= End || *Ptr == '}') { + if (Ptr < End) Ptr++; + break; } + if (*Ptr == '"') { + PCHAR KeyStart = ++Ptr; + while (Ptr < End && *Ptr != '"') Ptr++; + ULONG KeyLenStr = (ULONG)(Ptr - KeyStart); + if (Ptr < End) Ptr++; + SkipWhitespace(&Ptr, End); + if (Ptr < End && *Ptr == ':') Ptr++; + SkipWhitespace(&Ptr, End); - if (Ptr < End && *Ptr == '"') { - ULONG UTF8Len = (ULONG)(Ptr - StartQuote); - if (UTF8Len > 0) { - ULONG WideSize = 0; - RtlUTF8ToUnicodeN(NULL, 0, &WideSize, StartQuote, UTF8Len); - - if (WideSize > 0) { - PWCHAR WideBuffer = (PWCHAR)PyasAllocate(WideSize + sizeof(WCHAR)); - if (WideBuffer) { - ULONG ResultSize = 0; - RtlUTF8ToUnicodeN(WideBuffer, WideSize, &ResultSize, StartQuote, UTF8Len); - ULONG FinalSize = ProcessJsonUnescape(WideBuffer, ResultSize / sizeof(WCHAR)); - - UNICODE_STRING Us; - Us.Buffer = WideBuffer; - Us.Length = (USHORT)FinalSize; - Us.MaximumLength = (USHORT)(WideSize + sizeof(WCHAR)); - - AddRule(ListHead, &Us); - PyasFree(WideBuffer); - } + if (KeyLenStr == 4 && RtlCompareMemory(KeyStart, "Code", 4) == 4) Rule->Code = ParseInt(&Ptr, End); + else if (KeyLenStr == 9 && RtlCompareMemory(KeyStart, "Threshold", 9) == 9) Rule->Threshold = ParseInt(&Ptr, End); + else if (KeyLenStr == 10 && RtlCompareMemory(KeyStart, "TimeWindow", 10) == 10) Rule->TimeWindow = ParseInt(&Ptr, End); + else if (KeyLenStr == 8 && RtlCompareMemory(KeyStart, "Category", 8) == 8) { + if (*Ptr == '"') { + Ptr++; + if (RtlCompareMemory(Ptr, "Process", 7) == 7) Rule->Category = RuleCategoryProcess; + else if (RtlCompareMemory(Ptr, "File", 4) == 4) Rule->Category = RuleCategoryFile; + else if (RtlCompareMemory(Ptr, "Registry", 8) == 8) Rule->Category = RuleCategoryRegistry; + else if (RtlCompareMemory(Ptr, "Device", 6) == 6) Rule->Category = RuleCategoryDevice; + while (Ptr < End && *Ptr != '"') Ptr++; + if (Ptr < End) Ptr++; + } + } + else if (KeyLenStr == 9 && RtlCompareMemory(KeyStart, "Initiator", 9) == 9) ParseStringArray(&Ptr, End, &Rule->Initiator); + else if (KeyLenStr == 16 && RtlCompareMemory(KeyStart, "InitiatorExclude", 16) == 16) ParseStringArray(&Ptr, End, &Rule->InitiatorExclude); + else if (KeyLenStr == 6 && RtlCompareMemory(KeyStart, "Target", 6) == 6) ParseStringArray(&Ptr, End, &Rule->Target); + else if (KeyLenStr == 13 && RtlCompareMemory(KeyStart, "TargetExclude", 13) == 13) ParseStringArray(&Ptr, End, &Rule->TargetExclude); + else if (KeyLenStr == 11 && RtlCompareMemory(KeyStart, "CommandLine", 11) == 11) ParseStringArray(&Ptr, End, &Rule->CommandLine); + else if (KeyLenStr == 10 && RtlCompareMemory(KeyStart, "Extensions", 10) == 10) ParseStringArray(&Ptr, End, &Rule->Extensions); + else if (KeyLenStr == 10 && RtlCompareMemory(KeyStart, "Operations", 10) == 10) ParseOperationsArray(&Ptr, End, &Rule->Operations); + else { + BOOLEAN InQuote = FALSE, InArray = FALSE; + while (Ptr < End) { + if (*Ptr == '"' && *(Ptr - 1) != '\\') InQuote = !InQuote; + else if (!InQuote && *Ptr == '[') InArray = TRUE; + else if (!InQuote && *Ptr == ']') InArray = FALSE; + else if (!InQuote && !InArray && (*Ptr == ',' || *Ptr == '}')) break; + Ptr++; } } - Ptr++; } + else Ptr++; } - else { - Ptr++; - } + Rule->Next = g_DynamicRules; + g_DynamicRules = Rule; } - return; } + else Ptr++; } + break; } Ptr++; } } +BOOLEAN EvaluateDeviceRule(HANDLE ProcessId, PULONG OutCode) { + if (IsProcessTrusted(ProcessId)) return FALSE; + PUNICODE_STRING InitiatorPath = NULL; + GetProcessImageName(ProcessId, &InitiatorPath); + + BOOLEAN Blocked = FALSE; + KeEnterCriticalRegion(); + ExAcquireResourceSharedLite(&RuleLock, TRUE); + + PDYNAMIC_RULE Rule = g_DynamicRules; + while (Rule) { + if (Rule->Category == RuleCategoryDevice && (Rule->Operations & OP_IOCTL)) { + BOOLEAN Match = TRUE; + + if (Rule->Initiator && !MatchNodeListAny(Rule->Initiator, InitiatorPath)) Match = FALSE; + if (Match && Rule->InitiatorExclude && MatchNodeListAny(Rule->InitiatorExclude, InitiatorPath)) Match = FALSE; + + if (Match && CheckRuleThreshold(Rule, ProcessId)) { + *OutCode = Rule->Code; + Blocked = TRUE; + break; + } + } + Rule = Rule->Next; + } + + ExReleaseResourceLite(&RuleLock); + KeLeaveCriticalRegion(); + if (InitiatorPath) ExFreePool(InitiatorPath); + return Blocked; +} + VOID InitializeRulesEngine() { ExInitializeResourceLite(&RuleLock); KeInitializeSpinLock(&TrustCacheLock); RtlZeroMemory(TrustCache, sizeof(TrustCache)); + RtlZeroMemory(BehaviorTrackers, sizeof(BehaviorTrackers)); g_CacheInitialized = TRUE; } @@ -298,39 +402,10 @@ VOID UninitializeRulesEngine() { g_CacheInitialized = FALSE; } -static VOID RemoveRule(PRULE_NODE* Head, PUNICODE_STRING RuleStr) { - if (!RuleStr || !RuleStr->Buffer) return; - PRULE_NODE Current = *Head; - PRULE_NODE Previous = NULL; - - while (Current) { - if (RtlEqualUnicodeString(&Current->Pattern, RuleStr, TRUE)) { - PRULE_NODE ToDelete = Current; - - if (Previous) { - Previous->Next = Current->Next; - } - else { - *Head = Current->Next; - } - - Current = Current->Next; - - if (ToDelete->Pattern.Buffer) PyasFree(ToDelete->Pattern.Buffer); - PyasFree(ToDelete); - } - else { - Previous = Current; - Current = Current->Next; - } - } -} - VOID AddDynamicWhitelist(PUNICODE_STRING RuleStr) { KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite(&RuleLock, TRUE); AddRule(&g_ProcessTrustedPaths, RuleStr); - AddRule(&g_FileExceptionPaths, RuleStr); KIRQL OldIrql; KeAcquireSpinLock(&TrustCacheLock, &OldIrql); @@ -345,7 +420,6 @@ VOID RemoveDynamicWhitelist(PUNICODE_STRING RuleStr) { KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite(&RuleLock, TRUE); RemoveRule(&g_ProcessTrustedPaths, RuleStr); - RemoveRule(&g_FileExceptionPaths, RuleStr); KIRQL OldIrql; KeAcquireSpinLock(&TrustCacheLock, &OldIrql); @@ -371,7 +445,6 @@ NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) { UNICODE_STRING FinalPath = { 0 }; RtlInitUnicodeString(&ImagePathName, L"ImagePath"); - InitializeObjectAttributes(&RegOa, RegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); status = ZwOpenKey(&RegHandle, KEY_READ, &RegOa); if (!NT_SUCCESS(status)) return status; @@ -398,12 +471,9 @@ NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) { if (Info->Type == REG_EXPAND_SZ || Info->Type == REG_SZ) { PathBufferSize = Info->DataLength + 1024; PathBuffer = (PWCHAR)PyasAllocate(PathBufferSize); - if (PathBuffer) { RtlZeroMemory(PathBuffer, PathBufferSize); - if (Info->DataLength > 0) { - RtlCopyMemory(PathBuffer, Info->Data, Info->DataLength); - } + if (Info->DataLength > 0) RtlCopyMemory(PathBuffer, Info->Data, Info->DataLength); PWCHAR LastSlash = NULL; PWCHAR Current = PathBuffer; @@ -414,7 +484,6 @@ NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) { if (LastSlash) { *LastSlash = L'\0'; - SIZE_T CurrentPathLen = wcslen(PathBuffer); const WCHAR FilterSuffix[] = L"\\Filter"; SIZE_T FilterLen = (sizeof(FilterSuffix) / sizeof(WCHAR)) - 1; @@ -428,17 +497,12 @@ NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) { break; } } - if (Match) { - *SuffixStart = L'\0'; - } + if (Match) *SuffixStart = L'\0'; } RtlStringCbCatW(PathBuffer, PathBufferSize, L"\\Rules\\Rules_Driver_P1.json"); - if (wcsncmp(PathBuffer, L"\\??\\", 4) != 0 && - wcsncmp(PathBuffer, L"\\SystemRoot", 11) != 0 && - wcsncmp(PathBuffer, L"\\DosDevices\\", 12) != 0) { - + if (wcsncmp(PathBuffer, L"\\??\\", 4) != 0 && wcsncmp(PathBuffer, L"\\SystemRoot", 11) != 0 && wcsncmp(PathBuffer, L"\\DosDevices\\", 12) != 0) { PWCHAR TmpBuffer = (PWCHAR)PyasAllocate(PathBufferSize + 16); if (TmpBuffer) { RtlStringCbCopyW(TmpBuffer, PathBufferSize + 16, L"\\??\\"); @@ -471,16 +535,9 @@ NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) { status = ZwReadFile(FileHandle, NULL, NULL, NULL, &IoStatus, FileBuffer, FileInfo.EndOfFile.LowPart, NULL, NULL); if (NT_SUCCESS(status)) { ((PCHAR)FileBuffer)[FileInfo.EndOfFile.LowPart] = '\0'; - KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite(&RuleLock, TRUE); - ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Registry_BlockList", &g_RegistryBlockList); - ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Registry_TrustedList", &g_RegistryTrustedList); - ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Process_TrustedPaths", &g_ProcessTrustedPaths); - ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Process_ExploitableBlacklist", &g_ProcessExploitable); - ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_File_ProtectedPaths", &g_FileProtectedPaths); - ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_File_ExceptionPaths", &g_FileExceptionPaths); - ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_File_RansomwareExtensions", &g_FileRansomExts); + ParseDynamicRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart); ExReleaseResourceLite(&RuleLock); KeLeaveCriticalRegion(); } @@ -489,59 +546,95 @@ NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) { } ZwClose(FileHandle); } - if (PathBuffer) PyasFree(PathBuffer); return status; } +NTSTATUS LoadRuleFile(PCUNICODE_STRING FilePath) { + if (!FilePath || !FilePath->Buffer) return STATUS_INVALID_PARAMETER; + + NTSTATUS status = STATUS_SUCCESS; + HANDLE FileHandle = NULL; + IO_STATUS_BLOCK IoStatus = { 0 }; + OBJECT_ATTRIBUTES oa = { 0 }; + + InitializeObjectAttributes(&oa, (PUNICODE_STRING)FilePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); + status = ZwCreateFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &oa, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); + + if (NT_SUCCESS(status)) { + FILE_STANDARD_INFORMATION FileInfo = { 0 }; + status = ZwQueryInformationFile(FileHandle, &IoStatus, &FileInfo, sizeof(FileInfo), FileStandardInformation); + + if (NT_SUCCESS(status) && FileInfo.EndOfFile.LowPart > 0) { + PVOID FileBuffer = PyasAllocate(FileInfo.EndOfFile.LowPart + 1); + if (FileBuffer) { + status = ZwReadFile(FileHandle, NULL, NULL, NULL, &IoStatus, FileBuffer, FileInfo.EndOfFile.LowPart, NULL, NULL); + if (NT_SUCCESS(status)) { + ((PCHAR)FileBuffer)[FileInfo.EndOfFile.LowPart] = '\0'; + KeEnterCriticalRegion(); + ExAcquireResourceExclusiveLite(&RuleLock, TRUE); + ParseDynamicRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart); + ExReleaseResourceLite(&RuleLock); + KeLeaveCriticalRegion(); + } + PyasFree(FileBuffer); + } + else { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + ZwClose(FileHandle); + } + return status; +} + +VOID ClearDynamicRules() { + KeEnterCriticalRegion(); + ExAcquireResourceExclusiveLite(&RuleLock, TRUE); + PDYNAMIC_RULE CurrentRule = g_DynamicRules; + while (CurrentRule) { + PDYNAMIC_RULE Next = CurrentRule->Next; + FreeList(&CurrentRule->Initiator); + FreeList(&CurrentRule->InitiatorExclude); + FreeList(&CurrentRule->Target); + FreeList(&CurrentRule->TargetExclude); + FreeList(&CurrentRule->CommandLine); + FreeList(&CurrentRule->Extensions); + PyasFree(CurrentRule); + CurrentRule = Next; + } + g_DynamicRules = NULL; + ExReleaseResourceLite(&RuleLock); + KeLeaveCriticalRegion(); +} + VOID UnloadRules() { + ClearDynamicRules(); KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite(&RuleLock, TRUE); - FreeList(&g_RegistryBlockList); - FreeList(&g_RegistryTrustedList); FreeList(&g_ProcessTrustedPaths); - FreeList(&g_ProcessExploitable); - FreeList(&g_FileProtectedPaths); - FreeList(&g_FileExceptionPaths); - FreeList(&g_FileRansomExts); ExReleaseResourceLite(&RuleLock); KeLeaveCriticalRegion(); } NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName) { - if (KeGetCurrentIrql() != PASSIVE_LEVEL) { - return STATUS_UNSUCCESSFUL; - } - - NTSTATUS status; + if (KeGetCurrentIrql() != PASSIVE_LEVEL) return STATUS_UNSUCCESSFUL; PEPROCESS Process = NULL; - *ImageName = NULL; - status = PsLookupProcessByProcessId(ProcessId, &Process); + NTSTATUS status = PsLookupProcessByProcessId(ProcessId, &Process); if (!NT_SUCCESS(status)) return status; - status = SeLocateProcessImageName(Process, ImageName); ObDereferenceObject(Process); - return status; } -static BOOLEAN IsWindowsSystemApp(PCWSTR Buffer, USHORT Length) { - if (WildcardMatch(L"*\\Windows\\SystemApps\\*", Buffer, Length)) return TRUE; - if (WildcardMatch(L"*\\Windows\\ImmersiveControlPanel\\*", Buffer, Length)) return TRUE; - if (WildcardMatch(L"*\\Windows\\explorer.exe", Buffer, Length)) return TRUE; - return FALSE; -} - BOOLEAN IsProcessTrusted(HANDLE ProcessId) { ULONG PyasPid = SafeGetPyasPid(); if ((ULONG)(ULONG_PTR)ProcessId == PyasPid) return TRUE; if (ProcessId == (HANDLE)4) return TRUE; PEPROCESS Process = NULL; - if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process))) { - return FALSE; - } + if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process))) return FALSE; LARGE_INTEGER createTime; createTime.QuadPart = PsGetProcessCreateTimeQuadPart(Process); @@ -550,7 +643,6 @@ BOOLEAN IsProcessTrusted(HANDLE ProcessId) { KIRQL OldIrql; KeAcquireSpinLock(&TrustCacheLock, &OldIrql); ULONG Hash = (((ULONG)(ULONG_PTR)ProcessId) >> 2) & (TRUST_CACHE_SIZE - 1); - if (TrustCache[Hash].Process == Process && TrustCache[Hash].ProcessCreateTime.QuadPart == createTime.QuadPart) { LARGE_INTEGER Now; KeQuerySystemTime(&Now); @@ -573,34 +665,21 @@ BOOLEAN IsProcessTrusted(HANDLE ProcessId) { NTSTATUS status = GetProcessImageName(ProcessId, &imageFileName); BOOLEAN isTrusted = FALSE; - KeEnterCriticalRegion(); - ExAcquireResourceSharedLite(&RuleLock, TRUE); - if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) { - PRULE_NODE Node = g_ProcessExploitable; - while (Node) { - if (WildcardMatch(Node->Pattern.Buffer, imageFileName->Buffer, imageFileName->Length)) { - goto update_cache; - } - Node = Node->Next; - } - - if (IsWindowsSystemApp(imageFileName->Buffer, imageFileName->Length)) { - isTrusted = TRUE; - goto update_cache; - } - - Node = g_ProcessTrustedPaths; + KeEnterCriticalRegion(); + ExAcquireResourceSharedLite(&RuleLock, TRUE); + PRULE_NODE Node = g_ProcessTrustedPaths; while (Node) { if (WildcardMatch(Node->Pattern.Buffer, imageFileName->Buffer, imageFileName->Length)) { isTrusted = TRUE; - goto update_cache; + break; } Node = Node->Next; } + ExReleaseResourceLite(&RuleLock); + KeLeaveCriticalRegion(); } -update_cache: if (g_CacheInitialized) { KIRQL OldIrql; KeAcquireSpinLock(&TrustCacheLock, &OldIrql); @@ -612,274 +691,35 @@ update_cache: KeReleaseSpinLock(&TrustCacheLock, OldIrql); } - ExReleaseResourceLite(&RuleLock); - KeLeaveCriticalRegion(); - if (imageFileName) ExFreePool(imageFileName); ObDereferenceObject(Process); return isTrusted; } -BOOLEAN IsTargetProtected(HANDLE ProcessId) { - ULONG PyasPid = SafeGetPyasPid(); - if ((ULONG)(ULONG_PTR)ProcessId == PyasPid) return TRUE; - - if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE; - - PUNICODE_STRING imageFileName = NULL; - NTSTATUS status = GetProcessImageName(ProcessId, &imageFileName); - if (!NT_SUCCESS(status) || !imageFileName || !imageFileName->Buffer) { - return FALSE; - } - - BOOLEAN isProtected = FALSE; - - KeEnterCriticalRegion(); - ExAcquireResourceSharedLite(&RuleLock, TRUE); - - PRULE_NODE Node = g_FileProtectedPaths; - while (Node) { - if (WildcardMatch(Node->Pattern.Buffer, imageFileName->Buffer, imageFileName->Length)) { - isProtected = TRUE; - break; - } - Node = Node->Next; - } - - ExReleaseResourceLite(&RuleLock); - KeLeaveCriticalRegion(); - - if (imageFileName) ExFreePool(imageFileName); - return isProtected; -} - -BOOLEAN CheckRegistryRule(PCUNICODE_STRING KeyName) { - if (!KeyName || !KeyName->Buffer) return FALSE; - if (KeyName->Length < 4 * sizeof(WCHAR)) return FALSE; - - if (WildcardMatch(L"*{645FF040-5081-101B-9F08-00AA002F954E}\\DefaultIcon", KeyName->Buffer, KeyName->Length)) { - return FALSE; - } - - KeEnterCriticalRegion(); - ExAcquireResourceSharedLite(&RuleLock, TRUE); - - PRULE_NODE AllowNode = g_RegistryTrustedList; - while (AllowNode) { - if (WildcardMatch(AllowNode->Pattern.Buffer, KeyName->Buffer, KeyName->Length)) { - ExReleaseResourceLite(&RuleLock); - KeLeaveCriticalRegion(); - return FALSE; - } - AllowNode = AllowNode->Next; - } - - PRULE_NODE Node = g_RegistryBlockList; - BOOLEAN Match = FALSE; - while (Node) { - if (WildcardMatch(Node->Pattern.Buffer, KeyName->Buffer, KeyName->Length)) { - Match = TRUE; - break; - } - Node = Node->Next; - } - ExReleaseResourceLite(&RuleLock); - KeLeaveCriticalRegion(); - return Match; -} - -BOOLEAN CheckFileExtensionRule(PCUNICODE_STRING FileName) { - if (!FileName || !FileName->Buffer) return FALSE; - - KeEnterCriticalRegion(); - ExAcquireResourceSharedLite(&RuleLock, TRUE); - - PRULE_NODE ExNode = g_FileExceptionPaths; - while (ExNode) { - if (WildcardMatch(ExNode->Pattern.Buffer, FileName->Buffer, FileName->Length)) { - ExReleaseResourceLite(&RuleLock); - KeLeaveCriticalRegion(); - return FALSE; - } - ExNode = ExNode->Next; - } - - PRULE_NODE Node = g_FileRansomExts; - BOOLEAN Match = FALSE; - while (Node) { - if (HasSuffix(FileName, Node->Pattern.Buffer)) { - Match = TRUE; - break; - } - Node = Node->Next; - } - ExReleaseResourceLite(&RuleLock); - KeLeaveCriticalRegion(); - return Match; -} - -BOOLEAN CheckProtectedPathRule(PCUNICODE_STRING FileName) { - if (!FileName || !FileName->Buffer) return FALSE; - - if (WildcardMatch(L"*\\Windows\\System32\\config\\systemprofile*", FileName->Buffer, FileName->Length)) { - return FALSE; - } - - KeEnterCriticalRegion(); - ExAcquireResourceSharedLite(&RuleLock, TRUE); - - PRULE_NODE ExNode = g_FileExceptionPaths; - while (ExNode) { - if (WildcardMatch(ExNode->Pattern.Buffer, FileName->Buffer, FileName->Length)) { - ExReleaseResourceLite(&RuleLock); - KeLeaveCriticalRegion(); - return FALSE; - } - ExNode = ExNode->Next; - } - - PRULE_NODE Node = g_FileProtectedPaths; - BOOLEAN Match = FALSE; - while (Node) { - if (WildcardMatch(Node->Pattern.Buffer, FileName->Buffer, FileName->Length)) { - Match = TRUE; - break; - } - Node = Node->Next; - } - ExReleaseResourceLite(&RuleLock); - KeLeaveCriticalRegion(); - return Match; -} - -static BOOLEAN IsHighEntropy(PVOID Buffer, ULONG Length) { - if (!Buffer || Length < 256) return FALSE; - - PUSHORT Histogram = (PUSHORT)PyasAllocate(256 * sizeof(USHORT)); - if (!Histogram) return FALSE; - - PUCHAR Ptr = (PUCHAR)Buffer; - ULONG ScanLen = (Length > 1024) ? 1024 : Length; - - __try { - for (ULONG i = 0; i < ScanLen; i++) { - Histogram[Ptr[i]]++; - } - } - __except (EXCEPTION_EXECUTE_HANDLER) { - PyasFree(Histogram); - return FALSE; - } - - USHORT MaxFreq = 0; - for (int i = 0; i < 256; i++) { - if (Histogram[i] > MaxFreq) MaxFreq = Histogram[i]; - } - - ULONG ExpectedAvg = ScanLen / 256; - BOOLEAN Result = (MaxFreq < (ExpectedAvg + HIGH_ENTROPY_THRESHOLD)); - - PyasFree(Histogram); - return Result; -} - -static BOOLEAN IsNaturallyCompressed(PCUNICODE_STRING FileName) { - if (!FileName || !FileName->Buffer) return FALSE; - for (int i = 0; i < sizeof(Helper_NaturallyCompressedExtensions) / sizeof(Helper_NaturallyCompressedExtensions[0]); i++) { - if (HasSuffix(FileName, Helper_NaturallyCompressedExtensions[i])) return TRUE; - } - return FALSE; -} - -static BOOLEAN IsNoisyRansomPath(PCUNICODE_STRING FileName) { - if (!FileName || !FileName->Buffer) return FALSE; - - if (WildcardMatch(L"*\\Windows\\Temp\\*", FileName->Buffer, FileName->Length) || - WildcardMatch(L"*\\Windows\\SystemTemp\\*", FileName->Buffer, FileName->Length) || - WildcardMatch(L"*\\Windows\\Prefetch\\*", FileName->Buffer, FileName->Length) || - WildcardMatch(L"*\\Windows\\Logs\\*", FileName->Buffer, FileName->Length) || - WildcardMatch(L"*\\Profiles\\*", FileName->Buffer, FileName->Length) || - WildcardMatch(L"*\\Program*\\*", FileName->Buffer, FileName->Length) || - WildcardMatch(L"*\\AppData\\Local\\*", FileName->Buffer, FileName->Length) || - WildcardMatch(L"*\\User Data\\*", FileName->Buffer, FileName->Length) || - WildcardMatch(L"*$Recycle.Bin*", FileName->Buffer, FileName->Length)) { - return TRUE; - } - return FALSE; -} - -static BOOLEAN IsUserDirectoryPath(PCUNICODE_STRING FileName) { - if (!FileName || !FileName->Buffer) return FALSE; - return WildcardMatch(L"*\\Users\\*", FileName->Buffer, FileName->Length); -} - -static BOOLEAN IsExplorerProcess(HANDLE ProcessId) { - if (KeGetCurrentIrql() > APC_LEVEL) return FALSE; - - PUNICODE_STRING imageFileName = NULL; - NTSTATUS status = GetProcessImageName(ProcessId, &imageFileName); - BOOLEAN isExplorer = FALSE; - - if (NT_SUCCESS(status) && imageFileName) { - if (imageFileName->Buffer && HasSuffix(imageFileName, L"\\explorer.exe")) { - isExplorer = TRUE; - } - ExFreePool(imageFileName); - } - - return isExplorer; -} - -BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID WriteBuffer, ULONG WriteLength, BOOLEAN IsWrite) { - if (!IsUserDirectoryPath(FileName)) { - return FALSE; - } - - if (IsNoisyRansomPath(FileName)) { - return FALSE; - } - - if (!IsWrite) { - if (IsExplorerProcess(ProcessId)) return FALSE; - } - - BOOLEAN SuspiciousWrite = FALSE; - if (IsWrite && WriteBuffer && WriteLength > 0) { - if (!IsNaturallyCompressed(FileName) && IsHighEntropy(WriteBuffer, WriteLength)) { - SuspiciousWrite = TRUE; - } - } - - if (IsWrite && !SuspiciousWrite) { - return FALSE; - } +static BOOLEAN CheckRuleThreshold(PDYNAMIC_RULE Rule, HANDLE ProcessId) { + if (Rule->Threshold == 0) return TRUE; PEPROCESS Process = NULL; - if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process))) { - return FALSE; - } + if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process))) return FALSE; - LARGE_INTEGER Now = { 0 }; + LARGE_INTEGER Now; KeQuerySystemTime(&Now); LARGE_INTEGER createTime; createTime.QuadPart = PsGetProcessCreateTimeQuadPart(Process); - BOOLEAN Result = FALSE; + BOOLEAN Triggered = FALSE; KIRQL OldIrql; KeAcquireSpinLock(&GlobalData.TrackerMutex, &OldIrql); - PRANSOM_TRACKER Tracker = NULL; - PRANSOM_TRACKER CandidateSlot = NULL; - PRANSOM_TRACKER LruSlot = &RansomTrackers[0]; + PBEHAVIOR_TRACKER Tracker = NULL; + PBEHAVIOR_TRACKER CandidateSlot = NULL; + PBEHAVIOR_TRACKER LruSlot = &BehaviorTrackers[0]; for (int i = 0; i < MAX_TRACKERS; i++) { - PRANSOM_TRACKER Current = &RansomTrackers[i]; + PBEHAVIOR_TRACKER Current = &BehaviorTrackers[i]; + if (Current->LastActivityTime.QuadPart < LruSlot->LastActivityTime.QuadPart) LruSlot = Current; - if (Current->LastActivityTime.QuadPart < LruSlot->LastActivityTime.QuadPart) { - LruSlot = Current; - } - - if (Current->Process == Process && Current->ProcessCreateTime.QuadPart == createTime.QuadPart) { + if (Current->Process == Process && Current->Rule == Rule && Current->ProcessCreateTime.QuadPart == createTime.QuadPart) { Tracker = Current; break; } @@ -888,25 +728,15 @@ BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID Wr if (Current->Process != NULL) { LARGE_INTEGER Diff; Diff.QuadPart = Now.QuadPart - Current->LastActivityTime.QuadPart; - if (Diff.QuadPart > (RANSOM_TIME_WINDOW_MS * 10000LL)) { - IsExpired = TRUE; - } - } - - if ((Current->Process == NULL || IsExpired) && !CandidateSlot) { - CandidateSlot = Current; + if (Diff.QuadPart > (Rule->TimeWindow * 10000LL)) IsExpired = TRUE; } + if ((Current->Process == NULL || IsExpired) && !CandidateSlot) CandidateSlot = Current; } if (!Tracker) { - if (CandidateSlot) { - Tracker = CandidateSlot; - } - else { - Tracker = LruSlot; - } - + Tracker = CandidateSlot ? CandidateSlot : LruSlot; Tracker->Process = Process; + Tracker->Rule = Rule; Tracker->ProcessCreateTime = createTime; Tracker->ActivityCount = 0; Tracker->LastActivityTime = Now; @@ -914,31 +744,125 @@ BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID Wr else { LARGE_INTEGER Diff; Diff.QuadPart = Now.QuadPart - Tracker->LastActivityTime.QuadPart; - - if (Diff.QuadPart > (RANSOM_TIME_WINDOW_MS * 10000LL)) { - Tracker->ActivityCount = 0; - } + if (Diff.QuadPart > (Rule->TimeWindow * 10000LL)) Tracker->ActivityCount = 0; Tracker->LastActivityTime = Now; } - ULONG Weight = 2; - Tracker->ActivityCount += Weight; - - if (Tracker->ActivityCount >= RANSOM_COUNT_THRESHOLD) { - Result = TRUE; - } + Tracker->ActivityCount++; + if (Tracker->ActivityCount >= Rule->Threshold) Triggered = TRUE; KeReleaseSpinLock(&GlobalData.TrackerMutex, OldIrql); ObDereferenceObject(Process); - return Result; + return Triggered; +} + +BOOLEAN EvaluateProcessRule(HANDLE ProcessId, PCUNICODE_STRING TargetPath, PCUNICODE_STRING CommandLine, PULONG OutCode) { + if (IsProcessTrusted(ProcessId)) return FALSE; + PUNICODE_STRING InitiatorPath = NULL; + GetProcessImageName(ProcessId, &InitiatorPath); + + BOOLEAN Blocked = FALSE; + KeEnterCriticalRegion(); + ExAcquireResourceSharedLite(&RuleLock, TRUE); + + PDYNAMIC_RULE Rule = g_DynamicRules; + while (Rule) { + if (Rule->Category == RuleCategoryProcess && (Rule->Operations & OP_EXECUTE)) { + BOOLEAN Match = TRUE; + if (Rule->Initiator && !MatchNodeListAny(Rule->Initiator, InitiatorPath)) Match = FALSE; + if (Match && Rule->InitiatorExclude && MatchNodeListAny(Rule->InitiatorExclude, InitiatorPath)) Match = FALSE; + if (Match && Rule->Target && !MatchNodeListAny(Rule->Target, TargetPath)) Match = FALSE; + if (Match && Rule->TargetExclude && MatchNodeListAny(Rule->TargetExclude, TargetPath)) Match = FALSE; + if (Match && Rule->CommandLine && !MatchNodeListAny(Rule->CommandLine, CommandLine)) Match = FALSE; + + if (Match && CheckRuleThreshold(Rule, ProcessId)) { + *OutCode = Rule->Code; + Blocked = TRUE; + break; + } + } + Rule = Rule->Next; + } + + ExReleaseResourceLite(&RuleLock); + KeLeaveCriticalRegion(); + if (InitiatorPath) ExFreePool(InitiatorPath); + return Blocked; +} + +BOOLEAN EvaluateFileRule(HANDLE ProcessId, PCUNICODE_STRING TargetPath, ULONG Operation, PVOID WriteBuffer, ULONG WriteLength, PULONG OutCode) { + UNREFERENCED_PARAMETER(WriteBuffer); + UNREFERENCED_PARAMETER(WriteLength); + + if (IsProcessTrusted(ProcessId)) return FALSE; + PUNICODE_STRING InitiatorPath = NULL; + GetProcessImageName(ProcessId, &InitiatorPath); + + BOOLEAN Blocked = FALSE; + KeEnterCriticalRegion(); + ExAcquireResourceSharedLite(&RuleLock, TRUE); + + PDYNAMIC_RULE Rule = g_DynamicRules; + while (Rule) { + if (Rule->Category == RuleCategoryFile && (Rule->Operations & Operation)) { + BOOLEAN Match = TRUE; + if (Rule->Initiator && !MatchNodeListAny(Rule->Initiator, InitiatorPath)) Match = FALSE; + if (Match && Rule->InitiatorExclude && MatchNodeListAny(Rule->InitiatorExclude, InitiatorPath)) Match = FALSE; + if (Match && Rule->Target && !MatchNodeListAny(Rule->Target, TargetPath)) Match = FALSE; + if (Match && Rule->TargetExclude && MatchNodeListAny(Rule->TargetExclude, TargetPath)) Match = FALSE; + if (Match && Rule->Extensions && !MatchExtensionList(Rule->Extensions, TargetPath)) Match = FALSE; + + if (Match && CheckRuleThreshold(Rule, ProcessId)) { + *OutCode = Rule->Code; + Blocked = TRUE; + break; + } + } + Rule = Rule->Next; + } + + ExReleaseResourceLite(&RuleLock); + KeLeaveCriticalRegion(); + if (InitiatorPath) ExFreePool(InitiatorPath); + return Blocked; +} + +BOOLEAN EvaluateRegistryRule(HANDLE ProcessId, PCUNICODE_STRING KeyName, ULONG Operation, PULONG OutCode) { + if (IsProcessTrusted(ProcessId)) return FALSE; + PUNICODE_STRING InitiatorPath = NULL; + GetProcessImageName(ProcessId, &InitiatorPath); + + BOOLEAN Blocked = FALSE; + KeEnterCriticalRegion(); + ExAcquireResourceSharedLite(&RuleLock, TRUE); + + PDYNAMIC_RULE Rule = g_DynamicRules; + while (Rule) { + if (Rule->Category == RuleCategoryRegistry && (Rule->Operations & Operation)) { + BOOLEAN Match = TRUE; + if (Rule->Initiator && !MatchNodeListAny(Rule->Initiator, InitiatorPath)) Match = FALSE; + if (Match && Rule->InitiatorExclude && MatchNodeListAny(Rule->InitiatorExclude, InitiatorPath)) Match = FALSE; + if (Match && Rule->Target && !MatchNodeListAny(Rule->Target, KeyName)) Match = FALSE; + if (Match && Rule->TargetExclude && MatchNodeListAny(Rule->TargetExclude, KeyName)) Match = FALSE; + + if (Match && CheckRuleThreshold(Rule, ProcessId)) { + *OutCode = Rule->Code; + Blocked = TRUE; + break; + } + } + Rule = Rule->Next; + } + + ExReleaseResourceLite(&RuleLock); + KeLeaveCriticalRegion(); + if (InitiatorPath) ExFreePool(InitiatorPath); + return Blocked; } NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize) { if (KeGetCurrentIrql() > APC_LEVEL) return STATUS_UNSUCCESSFUL; - - if (!ExAcquireRundownProtection(&GlobalData.PortRundown)) { - return STATUS_PORT_DISCONNECTED; - } + if (!ExAcquireRundownProtection(&GlobalData.PortRundown)) return STATUS_PORT_DISCONNECTED; NTSTATUS status = STATUS_PORT_DISCONNECTED; @@ -964,22 +888,14 @@ NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize) } LARGE_INTEGER timeout; - if (KeGetCurrentIrql() == PASSIVE_LEVEL) { - timeout.QuadPart = -(5 * 10000); - } - else { - timeout.QuadPart = 0; - } - + timeout.QuadPart = (KeGetCurrentIrql() == PASSIVE_LEVEL) ? -(5 * 10000) : 0; status = FltSendMessage(GlobalData.FilterHandle, &GlobalData.ClientPort, msg, sizeof(PYAS_MESSAGE), NULL, NULL, &timeout); - PyasFree(msg); } else { status = STATUS_INSUFFICIENT_RESOURCES; } } - ExReleaseRundownProtection(&GlobalData.PortRundown); return status; } \ No newline at end of file