#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 TRUST_CACHE_SIZE = 128; constexpr auto TRUST_CACHE_TTL_SEC = 300; typedef struct _RANSOM_TRACKER { PEPROCESS Process; LARGE_INTEGER ProcessCreateTime; ULONG ActivityCount; LARGE_INTEGER LastActivityTime; } RANSOM_TRACKER, * PRANSOM_TRACKER; typedef struct _TRUST_CACHE_ENTRY { PEPROCESS Process; LARGE_INTEGER ProcessCreateTime; BOOLEAN IsTrusted; LARGE_INTEGER CacheTime; } TRUST_CACHE_ENTRY, * PTRUST_CACHE_ENTRY; static RANSOM_TRACKER RansomTrackers[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 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; 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) { PRULE_NODE Next = Current->Next; if (Current->Pattern.Buffer) PyasFree(Current->Pattern.Buffer); PyasFree(Current); Current = Next; } *Head = NULL; } 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; } 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 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++; } 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; } } 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; PCWSTR StringEnd = String + StringLenChars; while (String < StringEnd) { if (*Pattern == L'*') { mp = ++Pattern; cp = String + 1; } else if (*Pattern == L'?' || (RtlDowncaseUnicodeChar(*Pattern) == RtlDowncaseUnicodeChar(*String))) { Pattern++; String++; } else if (mp != NULL) { Pattern = mp; String = cp++; } else { return FALSE; } } while (*Pattern == L'*') { Pattern++; } return (*Pattern == L'\0') ? TRUE : FALSE; } static ULONG ProcessJsonUnescape(PWCHAR Buffer, ULONG LengthChars) { if (!Buffer || LengthChars == 0) return 0; ULONG WriteIdx = 0; ULONG 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; } 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++]; } } Buffer[WriteIdx] = L'\0'; return WriteIdx * sizeof(WCHAR); } static VOID SkipWhitespace(PCHAR* Ptr, PCHAR End) { while (*Ptr < End) { char c = **Ptr; if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { (*Ptr)++; } else { break; } } } static VOID ParseAndLoadRules(PCHAR JsonContent, ULONG ContentLength, PCSTR KeyName, PRULE_NODE* ListHead) { if (!JsonContent || !KeyName || !ListHead) return; 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; 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 == ']') { 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++; } 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); } } } Ptr++; } } else { Ptr++; } } return; } } } Ptr++; } } VOID InitializeRulesEngine() { ExInitializeResourceLite(&RuleLock); KeInitializeSpinLock(&TrustCacheLock); RtlZeroMemory(TrustCache, sizeof(TrustCache)); g_CacheInitialized = TRUE; } VOID UninitializeRulesEngine() { ExDeleteResourceLite(&RuleLock); 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); RtlZeroMemory(TrustCache, sizeof(TrustCache)); KeReleaseSpinLock(&TrustCacheLock, OldIrql); ExReleaseResourceLite(&RuleLock); KeLeaveCriticalRegion(); } VOID RemoveDynamicWhitelist(PUNICODE_STRING RuleStr) { KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite(&RuleLock, TRUE); RemoveRule(&g_ProcessTrustedPaths, RuleStr); RemoveRule(&g_FileExceptionPaths, RuleStr); KIRQL OldIrql; KeAcquireSpinLock(&TrustCacheLock, &OldIrql); RtlZeroMemory(TrustCache, sizeof(TrustCache)); KeReleaseSpinLock(&TrustCacheLock, OldIrql); ExReleaseResourceLite(&RuleLock); KeLeaveCriticalRegion(); } NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) { NTSTATUS status = STATUS_SUCCESS; HANDLE FileHandle = NULL; IO_STATUS_BLOCK IoStatus = { 0 }; OBJECT_ATTRIBUTES oa = { 0 }; UNICODE_STRING ImagePathName; OBJECT_ATTRIBUTES RegOa = { 0 }; HANDLE RegHandle = NULL; PKEY_VALUE_PARTIAL_INFORMATION Info = NULL; ULONG ResultLength = 0; PWCHAR PathBuffer = NULL; SIZE_T PathBufferSize = 0; 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; status = ZwQueryValueKey(RegHandle, &ImagePathName, KeyValuePartialInformation, NULL, 0, &ResultLength); if (status != STATUS_BUFFER_TOO_SMALL) { ZwClose(RegHandle); return status; } Info = (PKEY_VALUE_PARTIAL_INFORMATION)PyasAllocate(ResultLength); if (!Info) { ZwClose(RegHandle); return STATUS_INSUFFICIENT_RESOURCES; } status = ZwQueryValueKey(RegHandle, &ImagePathName, KeyValuePartialInformation, Info, ResultLength, &ResultLength); ZwClose(RegHandle); if (!NT_SUCCESS(status)) { PyasFree(Info); return status; } 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); } PWCHAR LastSlash = NULL; PWCHAR Current = PathBuffer; while (*Current) { if (*Current == L'\\') LastSlash = Current; Current++; } if (LastSlash) { *LastSlash = L'\0'; SIZE_T CurrentPathLen = wcslen(PathBuffer); const WCHAR FilterSuffix[] = L"\\Filter"; SIZE_T FilterLen = (sizeof(FilterSuffix) / sizeof(WCHAR)) - 1; if (CurrentPathLen >= FilterLen) { PWCHAR SuffixStart = PathBuffer + CurrentPathLen - FilterLen; BOOLEAN Match = TRUE; for (SIZE_T i = 0; i < FilterLen; i++) { if (RtlDowncaseUnicodeChar(SuffixStart[i]) != RtlDowncaseUnicodeChar(FilterSuffix[i])) { Match = FALSE; break; } } 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) { PWCHAR TmpBuffer = (PWCHAR)PyasAllocate(PathBufferSize + 16); if (TmpBuffer) { RtlStringCbCopyW(TmpBuffer, PathBufferSize + 16, L"\\??\\"); RtlStringCbCatW(TmpBuffer, PathBufferSize + 16, PathBuffer); PyasFree(PathBuffer); PathBuffer = TmpBuffer; } } RtlInitUnicodeString(&FinalPath, PathBuffer); } } } PyasFree(Info); if (!FinalPath.Buffer) { if (PathBuffer) PyasFree(PathBuffer); return STATUS_UNSUCCESSFUL; } InitializeObjectAttributes(&oa, &FinalPath, 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); 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); ExReleaseResourceLite(&RuleLock); KeLeaveCriticalRegion(); } PyasFree(FileBuffer); } } ZwClose(FileHandle); } if (PathBuffer) PyasFree(PathBuffer); return status; } VOID UnloadRules() { 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; PEPROCESS Process = NULL; *ImageName = NULL; 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) { if ((ULONG)(ULONG_PTR)ProcessId == GlobalData.PyasPid) return TRUE; if (ProcessId == (HANDLE)4) return TRUE; PEPROCESS Process = NULL; if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process))) { return FALSE; } LARGE_INTEGER createTime; createTime.QuadPart = PsGetProcessCreateTimeQuadPart(Process); if (g_CacheInitialized) { 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); if ((Now.QuadPart - TrustCache[Hash].CacheTime.QuadPart) < (TRUST_CACHE_TTL_SEC * 10000000LL)) { BOOLEAN cachedResult = TrustCache[Hash].IsTrusted; KeReleaseSpinLock(&TrustCacheLock, OldIrql); ObDereferenceObject(Process); return cachedResult; } } KeReleaseSpinLock(&TrustCacheLock, OldIrql); } if (KeGetCurrentIrql() != PASSIVE_LEVEL) { ObDereferenceObject(Process); return FALSE; } PUNICODE_STRING imageFileName = NULL; 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; while (Node) { if (WildcardMatch(Node->Pattern.Buffer, imageFileName->Buffer, imageFileName->Length)) { isTrusted = TRUE; goto update_cache; } Node = Node->Next; } } update_cache: if (g_CacheInitialized) { KIRQL OldIrql; KeAcquireSpinLock(&TrustCacheLock, &OldIrql); ULONG Hash = (((ULONG)(ULONG_PTR)ProcessId) >> 2) & (TRUST_CACHE_SIZE - 1); TrustCache[Hash].Process = Process; TrustCache[Hash].ProcessCreateTime = createTime; TrustCache[Hash].IsTrusted = isTrusted; KeQuerySystemTime(&TrustCache[Hash].CacheTime); KeReleaseSpinLock(&TrustCacheLock, OldIrql); } ExReleaseResourceLite(&RuleLock); KeLeaveCriticalRegion(); if (imageFileName) ExFreePool(imageFileName); ObDereferenceObject(Process); return isTrusted; } BOOLEAN IsTargetProtected(HANDLE ProcessId) { if ((ULONG)(ULONG_PTR)ProcessId == GlobalData.PyasPid) return TRUE; return FALSE; } 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; } PEPROCESS Process = NULL; if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process))) { return FALSE; } LARGE_INTEGER Now = { 0 }; KeQuerySystemTime(&Now); LARGE_INTEGER createTime; createTime.QuadPart = PsGetProcessCreateTimeQuadPart(Process); BOOLEAN Result = FALSE; KIRQL OldIrql; KeAcquireSpinLock(&GlobalData.TrackerMutex, &OldIrql); PRANSOM_TRACKER Tracker = NULL; PRANSOM_TRACKER CandidateSlot = NULL; PRANSOM_TRACKER LruSlot = &RansomTrackers[0]; for (int i = 0; i < MAX_TRACKERS; i++) { PRANSOM_TRACKER Current = &RansomTrackers[i]; if (Current->LastActivityTime.QuadPart < LruSlot->LastActivityTime.QuadPart) { LruSlot = Current; } if (Current->Process == Process && Current->ProcessCreateTime.QuadPart == createTime.QuadPart) { Tracker = Current; break; } BOOLEAN IsExpired = FALSE; 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 (!Tracker) { if (CandidateSlot) { Tracker = CandidateSlot; } else { Tracker = LruSlot; } Tracker->Process = Process; Tracker->ProcessCreateTime = createTime; Tracker->ActivityCount = 0; Tracker->LastActivityTime = Now; } else { LARGE_INTEGER Diff; Diff.QuadPart = Now.QuadPart - Tracker->LastActivityTime.QuadPart; if (Diff.QuadPart > (RANSOM_TIME_WINDOW_MS * 10000LL)) { Tracker->ActivityCount = 0; } Tracker->LastActivityTime = Now; } ULONG Weight = 2; Tracker->ActivityCount += Weight; if (Tracker->ActivityCount >= RANSOM_COUNT_THRESHOLD) { Result = TRUE; } KeReleaseSpinLock(&GlobalData.TrackerMutex, OldIrql); ObDereferenceObject(Process); return Result; } 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; } NTSTATUS status = STATUS_PORT_DISCONNECTED; if (GlobalData.ClientPort) { PPYAS_MESSAGE msg = (PPYAS_MESSAGE)PyasAllocate(sizeof(PYAS_MESSAGE)); if (msg) { msg->MessageCode = Code; msg->ProcessId = Pid; if (Path && PathSize > 0) { size_t MaxSize = sizeof(msg->Path) - sizeof(WCHAR); size_t BytesToCopy = PathSize > MaxSize ? MaxSize : PathSize; __try { RtlCopyMemory(msg->Path, Path, BytesToCopy); msg->Path[BytesToCopy / sizeof(WCHAR)] = L'\0'; } __except (EXCEPTION_EXECUTE_HANDLER) { PyasFree(msg); ExReleaseRundownProtection(&GlobalData.PortRundown); return STATUS_ACCESS_VIOLATION; } } LARGE_INTEGER timeout; if (KeGetCurrentIrql() == PASSIVE_LEVEL) { timeout.QuadPart = -(5 * 10000); } else { timeout.QuadPart = 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; }