VIRUSKOV / WIKI / TESPİT KURALLARI / MLE_RANSOM_BEHAVIOR
MLE_RANSOM_BEHAVIOR
Fidye Yazılımı Tespit Kuralının Tam Anatomisi
OpenEDR × Hydra Dragon çekirdeği • PTM / PatternsMatching v2 politika motoru
Kural Kimliği: RANSOM_BEHAVIOR | Yayın Olay Tipi: MLE_RANSOM_BEHAVIOR | baseType: 1000008
Politika Kaynağı: OpenEDR/edrav2/iprj/edrdata/ptm.local.src • Satır 4636 – 5129
MLE_RANSOM_BEHAVIOR, bir dosyanın hash'ine, derleyicisine ya da paketleyicisine bakmaz.
Çekirdekte gördüğü tek şey davranıştır: bir süreç, kullanıcının kişisel
dosyalarına 60 saniye içinde en az 3 ayrı hedefe yazıyor, yeniden adlandırıyor
ya da siliyor; bu hedefler metin dosyası değil ve şifrelenmiş gibi
yüksek entropili veri taşıyor. Bu üçlü — kitle × hacim × içerik
— eşleştiği anda motor, saldırganın proses imajını karantinaya alır ve
süreci anında öldürür. Bu sayfa, bunun her tek satırını, her tek süzgecini ve
her bilinen sınırını açar.
01 Künye
Aşağıdaki tablo, kuralın kimlik bilgilerini ve teknik parametrelerini tek yerde toplar.
Bu değerlerin tamamı depodaki kaynak koddan birebir alınmıştır; hiçbiri
tahmin değildir. Kaynak satırları ve dosya yolları her hücrede verilmiştir.
Kural Adı (policy pattern)
RANSOM_BEHAVIOR
Yayın Olayı (createEvent)
MLE_RANSOM_BEHAVIOR
baseType (eventBaseType)
1000008
Kural Tipi
DAVRANIŞ TANI / POLİTİKA (behavior)
Politika Grubu
patternsMatching
Politika Bağımlılığı
common (version 2)
Motor
PTM v2 — PatternsMatching / EVM
Kaynak Dosya
OpenEDR/edrav2/iprj/edrdata/ptm.local.src
Satır Aralığı
4636 – 5129 (toplam 16 kural maddesi)
Aşama Sayısı
5 mantıksal aşama: KAYIT → ADAY → DEĞERLENDİRME → KARAR → TEMİZLİK
Süzgeç Sayısı (aşama 4)
9 bağımsız filtre, tek AND zincirinde
Sayaç Eşiği
3 AYRI hedef dosya
Sayaç Penceresi
60000 ms (60 saniye) kayan pencere
Kaynak Olay Sayısı
10 farklı kernel dosya olayı tetikler
Beyaz Liste
8 sistem yolu kalıbı
Uzantı Gözcüsü
1479 kullanıcı dosyası uzantısı
Güvenlik Seviyesi
3 — CRITICAL (kaynak kodda sabit)
Varsayılan Yanıt
KARANTİNA + SÜRECİ ÖLDÜR + GERİ YÜKLE
16
Kural Maddesi
10
Kernel Tetikleyicisi
9
Karar Süzgütü
1479
İzlenen Uzantı
3 / 60s
Eşik / Pencere
Ring-0
Tespit Katmanı
02 Bir Cümlede
Yeni veya imzası bilinmeyen bir fidye yazılımı, sisteme indirilip
çalıştırıldığı andan itibaren derlenmiş kod imzasına bakılmadan tespit
edilir. Çünkü kural, kodun ne yaptığına bakar:
Kernel dosya sistemi süzgeci (minifilter), 60 saniye içinde 3 veya daha fazla ayrı kullanıcı dosyasına yazma / yeniden adlandırma / silme işlemi yapan bir süreci kayda geçirir.
Politika motoru o dosyaların metin olmadığını (ilk 8 KiB'lık ASCII taramasıyla) ve şifreli veri gibi yüksek entropili olduğunu doğrular.
Hedeflerin kullanıcı profilinde (C:\Users\...) ve AppData / Temp / Windows / Program Files dışında olduğunu doğrular.
Tüm koşullar tutarsa MLE_RANSOM_BEHAVIOR olayı üretilir; motor sürecin imaj dosyasını karantinaya alır ve süreci Global ID ile öldürür.
Kernel'in RansomShield modülü daha önce kaydettiği özgün dosya kopyalarını geri yükler.
▶ Bunun pratik anlamı
Fidye yazılımı ailesi dün ya da bugün derlenmiş olsun, paketlenmiş olsun,
ismi hiç görülmemiş bir isim taşısın, farkı yoktur. Kural bir aileyi
değil, bir davranışı tarif eder. Bu yüzden imza veritabanını güncellemeye
gerek kalmadan sıfırıncı gün fidye yazılımı da aynı kapıdan geçer.
03 Neden Bu Kural Bir İmza Değil, Bir Davranış Kuralı?
Klasik antivirüs düşüncesi şöyledir: “Bu dosyayı daha önce gördüm, SHA-256'sı
listede var, zararlı.” Bu yöntem iki yerde kırılır.
Yaklaşım
Ne yapıyor
Kırılma noktası
Hash imzası
Dosyanın özetini sabit bir listede arar
Tek bir bayt değişse hash değişir. Atlatma: 1 saniye.
Yeni sürüm derleyici imzayı değiştirir. Atlatma: tek güncelleme.
Yapay zekâ destekli statik ML
PE başlıklarından sapma skorlar
İyi yazılmış zararlı, meşru PE başlığını taklit eder. Zayıf ayrım.
MLE_RANSOM_BEHAVIOR
Dosya sistemi I/O akışını izler, şifreleme işlemini kendi hâlinde tanır
Kod değişse de davranış değişmez. Şifreleme zorunlu bir adımdır.
▶ Temel fikir
Fidye yazılımının tek değişmez zorunluluğu vardır: kullanıcının dosyasını okumak
ve geri döndürülemez biçimde bozmak.
Kod imzası, paketleyici, dosya adı, hata mesajı, uzantı — hepsi değişebilir.
Ama “onlarca kullanıcı dosyasına arka arkaya yazıp geri döndürülemez hâle getirmek”
eylemi değişmez. Kural tam olarak bu eylemi imzalar.
04 Uçtan Uca Mimari: Olayın Yolculuğu
Kuralın arkasında sekiz ayrı katman var. Olayın yolculuğunu baştan sona izleyelim.
[1] RING-0 — edrdrv.sys (minifilter)
IRP_MJ_CREATE / CLEANUP / WRITE / READ / SET_INFORMATION
IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION (mmap yakalama)
|
| LLE_FILE_CREATE / LLE_FILE_DATA_READ_FULL / LLE_FILE_DATA_WRITE_FULL
| LLE_FILE_MAP_READ / LLE_FILE_MAP_WRITE / LLE_FILE_DATA_CHANGE
| LLE_FILE_RENAME / LLE_FILE_DELETE
v
[2] LBVS WIRE PROTOCOL (libsysmon/inc/edrdrvapi.hpp)
Sema tabanlı, çözümlenmiş adlandırılmış byte akışı
|
v
[3] libsysmon CONTROLLER (controller.cpp)
SysmonEvent -> Event enum eşlemesi, "type" etiketi
|
v
[4] EventEnricher (eventenricher.cpp)
Süreç zenginleştirme, processes[] zinciri, dosya nesnesi
-> file alanları TAMBEL PROXY (64KB okuma sadece istendiğinde)
|
v
[5] KUYRUK: match_patterns (qsc/match_patterns.qsc)
|
v
[6] PTM POLİTİKA MOTORU (ptm.local.src / RANSOM_BEHAVIOR)
|
| destination "IN" -> geri dönüş match_patterns (durum makinesi)
| destination "OUT" -> enrich_mle kuyruğu (tespit çıkışı)
v
[7] enrich_mle -> check_for_valkyrie -> transform_for_cloud -> output
|
v
[8] DetectionNotifier (detectionnotifier.cpp)
Güven kontrolü -> GID ile ÖLDÜR -> KARANTİNA -> GERİ YÜKLE -> GUI
Kritik ayrım şudur: destination: "IN" olayı motorun icine tutar
(bu bir tespit değil, bir durum makinesi adımıdır); destination: "OUT" ise
olayı motor dışına, zenginleştirme ve yanıt zincirine çıkarır.
MLE_RANSOM_BEHAVIOR OUT ile yayılır — yani gerçek
bir tespittir.
▶ Neden iki kuyruk?
IN olayları aynı kuyruğa geri beslenir; böylece politika dosyası
olay üretip kendi kuralına geri göndererek çok aşamalı bir durum makinesi
kurabilir. Bu, tek bir dosya işlemi üzerinde “oku → yaz → say → karar ver”
zinciri kurmanın en temiz yoludur ve tam olarak MLE_RANSOM_BEHAVIOR'ın mimarisidir.
05 Altyapı: PTM (PatternsMatching) Motoru
Kural, JSON tabanlı bir politika dilinde tanımlıdır. Derlendiğinde ürüne gömülür ve
denetlenebilir kalır — yani bu sayfadaki her satır, üründe çalışan
gerçek kodun satırıdır. Motor C++ ile yazılmıştır
(libedr/src/policy_v2.cpp, policycompiler.cpp,
policyoperation.cpp, signcontext.cpp).
5.1 — Yönergeler (Directives)
Her kural maddesi, bir "rule" sözlüğü içinde yönergelerden oluşur:
Yönerge
Ne yapar
Bu Kuralda Nerede
condition
Filtre. Sağlanmazsa kural her zaman eşleşir.
Aşama 1a/1b ve aşama 5'te yok; aday üretimi ve karar kurallarında var
saveContext
Sepet (basket) içine anahtar + veri + yaşam süresi (ms) ile kayıt yazar.
Aşama 1a, 1b — RANSOM_FILE_READ_WRITE sepeti
loadContext
Sepetten okur. @context.* kullanımı bunu zorunlu kılar.
Bu kuralda kullanılmıyor (bkz. Bölüm 16)
deleteContext
Sepetten anahtar veya grup siler.
Aşama 5 — süreç sonlandığında
createEvent
Yeni olay üretir. destination: IN / OUT / IN_OUT. clone:false ise yalnızca data'daki alanlar taşınır.
Aşama 2, 3 ve 4 — kuralın kalbi
distinctCounter
Kayan pencerede ayrı değer sayar, eşik aşılınca flag alanına boolean yazar.
Aşama 4 — ransomFileDistinct sepeti
discard
Olayı düşürür.
Kullanılmıyor
▶ Kritik motor kısıtı: kural başına tek condition
Bir kural sözlüğünde yalnızca bir condition anahtarı olabilir.
Motor yönergeleri bir unordered_map<Directive, DirectivePtr> içinde tutar;
ikinci bir condition yazılırsa ilk sessizce düşer. Bu yüzden aşama 4'teki
dokuz süzgecin tamamı tek bir "and" zincirinde birleştirilmiştir.
Bu, kuralın yazılış biçiminin bir kısıtından doğan bilinçli bir tasarım kararıdır ve
ptm.local.src içinde de açıkça belgelenmiştir.
5.2 — Operasyonlar ve Değerlendirme Semantiği
Operasyon
Arity
Anlamı
Bu Kuralda
imatch
1 değer + pattern
Büyük/küçük harf duyarsız joker eşlemesi
*\users\* vb.
!imatch
1 değer + pattern
Eşleşmezse true
Beyaz liste kontrolleri
and / or
≥ 2
Mantıksal birleştirme (kısa devre yok, hepsi değerlendirilir)
Süzgeç zinciri
equal / !equal
tam 2
Tip bazlı eşitlik
type == "OTHER", isAsciiText
greater
tam 2
Tam sayı karşılaştırma; null/bool/string → 0 sayılır
Entropi eşiği
5.3 — Joker (Wildcard) Eşleme Semantiği
imatch operatörü joker kalıpları iki ayrı motora dağıtır ve
bu seçim performansı doğrudan etkiler:
Kalıp Biçimi
Derlenen Motor
Anlam
*\Windows\*
StringMatcher, mod all
“içinde \Windows\ geçiyor” — hızlı yol, regex yok
*.docx
StringMatcher, mod end
“sonu .docx ile bitiyor” — 1479 uzantı bu yolda
C:\Windows\explorer.exe
StringMatcher, mod begin
“ile başlıyor” (yani .exe.bak de eşleşir)
*\*.*.*
POSIX regex (temel)
Ortada joker varsa regex yoluna düşer; *→.*, ?→.
▶ Önemli ayrım
Baştaki ve sondaki * karakterleri birer “regex jokeri” değildir;
bunlar çapa (anchor) görevi görür ve begin / end /
all modlarına dönüştürülür. 1479 uzantılık liste bu yüzden regex derlenmeden,
tek bir StringMatcher tablosu olarak çalışır — bu, canlı sistemde
onlarca mikrosaniyelik maliyet demektir.
06 Kuralın Anatomisi: 16 Madde, 5 Aşama
RANSOM_BEHAVIOR deseni 16 kural maddesinden oluşur. Bu maddeler beş mantıksal
aşama hâlinde gruplanır. Aşağıda her aşama, ilgili kaynak kodla birlikte açıklanmıştır.
AŞAMA 1Okuma Tarafının Kaydedilmesi — saveContext
İki kural maddesi, sistem dışı bir dosyanın tam olarak okunduğunu kayda
geçirir. Amaç “bu süreç şu dosyayı okudu” bilgisini 120 saniye boyunca
saklamaktır. Bu, ileride “okuyan → yazan” eşleşmesi kurulabilmesi için
temeldir (bkz. Bölüm 16, madde 3: bu sepet şu an yalnızca yazılır, okunmaz).
Madde 1a — LLE_FILE_DATA_READ_FULL
Dosya, eksiksiz ve sıralı biçimde baştan sona okunduğunda üretilen kernel
olayını yakalar. Minifilter bunu ancak readInfo.nNextPos == nSizeAtCreation
koşulu sağlandığında yapar — yani kısmi okumalar sayılmaz.
Madde 1b — LLE_FILE_MAP_READ (bellek eşleme ile okuma)
Aynı iş, haritalama (mmap) yoluyla yapılıyorsa. Birçok modern fidye yazılımı
dosyayı CreateFile + ReadFile ile değil,
NtCreateSection + NtMapViewOfSection ile belleğe eşler. Bu yol
hiç IRP_MJ_READ göndermez; dolayısıyla yukarıdaki madde 1a
onu yakalayamaz. Minifilter bu yüzden
IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION IRP'sinin
PAGE_READONLY sayfa korumasını ayrı bir olaya çevirir.
▶ Neden bu ayrı madde önemli?
LLE_FILE_MAP_READ ve LLE_FILE_MAP_WRITE maddeleri olmasaydı,
haritalama tabanlı şifreleyiciler — Rust/Go/C++ ile yazılmış modern fidye
yazılımlarının büyük bölümü — tamamen görünmez olurdu. İki satır,
kuralı “okuma yapan herkesi yakalar” seviyesine taşıyan asıl teknik karardır.
AŞAMA 2Aday Olayın Üretimi — MLE_RANSOM_CANDIDATE
On maddelik bu blok, motorun kalbidir. Sistem dışı bir dosya üzerinde
her türlü yazma, değiştirme, yeniden adlandırma veya silme eylemi
MLE_RANSOM_CANDIDATE olayına dönüştürülür. Burada hiçbir karar
verilmez; sadece olay kayda geçer.
destination: "IN" olduğu için bu olay motorun içinde kalır ve aşama 3'te
tekrar işlenmek üzere match_patterns kuyruğuna geri beslenir.
Madde
Kaynak Olay
Ne Zaman Üretilir (Kernel)
Not
2a
LLE_FILE_CREATE
IRP_MJ_CREATE sonrası — dosya oluşturuldu veya kırpıldı
Yeni şifreli dosya oluşturan modelin ilk adımı
2b
LLE_FILE_DATA_WRITE_FULL
IRP_MJ_CLEANUP sonrası — dosya baştan sona yazıldı
Varsayılan clone: true olsaydı, tetikleyen olayın tüm alanları
yeni olaya otomatik kopyalanırdı. clone: false ile motor yalnızca
data dizisinde elle listelenen alanları taşır. Bu, zincirin
her adımında hangi alanın bilinçli olarak taşındığını (ve hangisinin bilinçli
olarak taşınmadığını) görünür kılar — yani politikanın okunabilirliği ve
denetlenebilirliği kazanılır.
MLE_RANSOM_CANDIDATE olayını alan motor, onu
MLE_RANSOM_EVALUATE olayına dönüştürür. Bu tek maddelik kural,
kararın kendisinden ayrılmış saf bir adım olarak tasarlanmıştır ve
üç şey yapar:
Etiket değişikliği: artık bu olay “bir dosyaya dokunuldu” değil, “şüpheli dosya”dır.
Alan normalizasyonu: destination alanı, aşama 2'de @event.file iken burada @event.destination olarak okunur — yani önceki olayın taşıdığı alan.
Zaman damgası taşıma: time ve tickTime korunur, böylece sayaç ve analiz zaman çizelgesi doğru kalır.
AŞAMA 4Karar — distinctCounter + Dokuz Süzgeç → MLE_RANSOM_BEHAVIOR
Kuralın kalbi. Burada iki şey aynı anda olur:
distinctCounter çalışır: süreç kimliği (PID) başına, son 60 saniyede kaç ayrı hedef dosyaya dokunulduğu sayılır. 3'e ulaşınca @event.RansomThresholdMet = true yazılır.
condition çalışır: dokuz süzgecin tamamı tutmalıdır. Tutan tutarsa nihai tespit olayı OUT ile dışarı çıkar.
▶ Değerlendirme sırası: sayaç ÖNCE, koşul SONRA
Derleyici yönergeleri sabit bir sırada üretir:
distinctCounter → condition.
Yani herMLE_RANSOM_EVALUATE sayacı besler — koşul daha
sonra değerlendirilir. Bunun pratik sonucu Bölüm 16'da ayrıntılı olarak ele alınmıştır;
şimdilik şunu bilin: sayaç “potansiyel aday” sayar, kesin şüpheliyi değil.
ptm.local.src — AŞAMA 4, createEvent (5100-5116)createEvent / OUT — GERÇEK TESPİT
Aday aşamasında quarantineTargetşifrelenen dosyayı işaret
ederdi. Nihai tespitte ise @event.process.imageFile.rawPath — yani
saldırganın kendi çalıştırılabilir dosyasının yolu — atanır.
Bu, tüm kuralın en önemli mimari kararıdır: mağdur olan dosya değil, suç işleyen
sürektir. Karantina, fidye yazılımının ikili dosyasına uygulanır; kullanıcının
verisi RansomShield tarafından geri yüklenir.
AŞAMA 5Bağlam Temizliği — deleteContext
Süreç sonlandığında (LLE_PROCESS_DELETE) okuma sepeti temizlenmeye çalışılır.
Bu, uzun süre çalışan bir makinede belleğin şişmesini önleyen hijyen maddesidir.
Aşama 1a/1b kayıtları group alanı olmadan yazılırken, aşama 5
group ile silmeyi dener. Motor bu eşleşmeyi kuramadığı için temizlik
pratikte 120 saniyelik TTL üzerinden gerçekleşir. Bu, işlevsel bir
sorun değildir; kuralı etkilemez, yalnızca kaydın yaşam süresi belirleyicidir.
Ayrıntı Bölüm 16'da.
07 Dokuz Süzgeç, Tek Tek
Aşama 4'ün condition alanı, tek bir "and" zincirinde
birleştirilmiş dokuz bağımsız filtredir. Kural tetiklenmesi için hepsinin
aynı anda tutması gerekir. Bu, varsayılan olarak kapalı (fail-safe) bir
tasarımdır: yanlış pozitif değil, kaçırılan tespit üzerine kuruludur.
Aktör beyaz listesi. “Şifreleyen süreç” kontrol edilir,
“şifrelenen dosya” değil. C:\Windows\,
C:\Program Files\ altından çalışan meşru araçlar (güncelleyiciler, kurulum
sihirbazları, antivirüs motorları) bu süzgeçle elenir.
Neden aktör kontrolü? Bir kurulum sihirbazı
C:\Users\alice\Desktop altına da yazabilir; ama
C:\Windows\Temp\ içindeki bir setup.exe onlarca kullanıcı
dosyasına yazmaz. Aktörü elemek, hedefe bakmaktan çok daha güvenilir
bir yanlış pozitif kesicidir.
Hedef beyaz listesi. Hedef yolun kendisi 8 sistem kalıbından birine
benzememeli. Bkz. Bölüm 11. Bu süzgeç olmadan güncelleme motorları,
tarayıcı önbellekleri ve kurulum önbellekleri kuralı
periyodik olarak tetiklerdi.
S3imatch("*\\users\\*", @event.destination.path) — ZORUNLU (+)
Coğrafya daraltması. Tek zorunlu pozitif koşul budur: hedef
bir kullanıcı profilinin içinde olmak zorundadır. Fidye yazılımının
neden şifrelediği bellidir — C:\Users\alice\Documents,
Desktop, Downloads, Photos gibi kişisel veri
yığınları. C:\Windows veya ağ sürücülerini şifreleyen bir araç
kural tarafından hedeflenmez. Bu tek koşul, kurumsal masaüstünde yanlış
pozitif oranını dramatik biçimde düşürür.
Not: Bu kalıp StringMatcher “içinde geçiyor”
modunda derlenir. Kurulum adı her zaman İngilizce kalır (\Users\), bu yüzden
yerelleştirilmiş Windows kurulumlarında da çalışır.
Uygulama verisi koruması.AppData içinde Chrome, Firefox,
VS Code, Steam, Discord gibi yüzlerce uygulama sürekli olarak onlarca
dosyaya yazar. Bunlar “çok sayıda dosyaya yazan meşru süreçler”dir ve
kullanıcının asıl değerli verisi burada değildir. Bu süzgeç, en gürültülü
yanlış pozitif kaynağını kapatır.
Çelişki notu: S3 \users\ içinde \AppData\'yu
arar; S2 zaten *\AppData\* kalıbını içerir. Dolayısıyla S4, S2 ile
birebir aynı kontrolü yapar ve bugün itibarıyla ek bir filtre
değildir — savunma derinliği ve okunabilirlik için bilinçli olarak
tekrarlanmıştır.
Mark-of-the-Web koruması. NTFS alternatif veri akışlarındaki
Zone.Identifier, “bu dosya internetten indirildi” işaretidir.
Marka yöntemi (Mark of the Web) saldırıları tam olarak bu akışı kullanır:
indirilen bir dosyanın MOTW akışına dokunarak kurumsal dosyaları “internet kökenli”
hâle getirir ve kullanıcıyı uyarıyla kandırır. Bu süzgeç, bu tür teknikleri kuralın
kapsamı dışına iter.
S6equal(@event.destination.type, "OTHER")
Hedef bir çalıştırılabilir değil. Dosya tipi, uzantının yapılandırılmış
çalıştırılabilir listesine (.exe;.dll;.sys;.cpl;.mui;.tlb;.scr) girip
girmemesine göre belirlenir. Kural yalnızca "OTHER" hedefleri kabul eder,
yani belge, tablo, görsel, video, arşiv, kaynak kodu ve veritabanı.
Neden önemli? Bir zararlının kendi ikili dosyasını değiştirmesi
(kalıcılık kazanmak için) bu kuralı tetiklemez — bu tür eylemler
MLE_PUA_REGISTRY_WRITE ve MLE_FILE_COPY gibi komşu kuralların
alanıdır. Kuralın tek bir amacı vardır: kullanıcı verisini imha etmek.
Tek amaç, düşük yanlış pozitif demektir.
S7uzantı var AND kullanıcı dosyası uzantısı AND entropi > 6
Üç alt koşulun AND&OR yapısıdır. 7a: hedefte (veya kaynakta) bir
nokta bulunmalı — uzantısız geçici dosyalar elenir. 7b: uzantı
1479 kalıplı kullanıcı verisi listesinden biri olmalı. 7c:
Shannon entropisi eşiği aşmalı. Bu üçlü, kuralın “bu bir veri dosyası mı, bu
şifrelenmiş mi?” sorusuna cevap verir.
Pratik sonuç:C:\Users\alice\Notes.txt dosyasına düz metin
yazan bir metin editörü elenir (7b: .txt listede, ama 8. süzgeç
isAsciiText=true → elenir). Aynı dosyayı şifreleyen fidye yazılımı
geçer.
En güçlü tek süzgeç. Dosyanın diskteki güncel ilk
64 KiB'ı okunur, ancak metin olup olmadığına yalnızca ilk 8192 bayta (8 KiB)
bakılarak karar verilir: \t \n \r ve 0x20-0x7E dışındaki
baytların sayısı 8 KiB × %15 + 1 = 1229 eşiğine ulaşırsa dosya
“metin değildir” olarak işaretlenir (fail-fast).
Neyi okur, ne zaman okur? Alan, kuralın değerlendirildiği
andaki diskteki güncel içeriği okur — yani saldırganın az önce yazdığı
ciphertext. Uzantıya, dosya adına veya “şüpheli dosya”
etiketine bakmaz. Aşama 4 çoğunlukla LLE_FILE_DATA_WRITE_FULL üzerinde
çalıştığı için dosya bu anda şifrelenmiş hâldedir ve tarama şifre metnini
görür. Aynı süzgeç, metin editörünün aynı dosyaya yazdığı düz metni ise eler.
Ölçüm penceresi: 8 KiB, eşik %15 + 1, fail-fast. Uygulama 64 KiB'a
kadar tek seferde okur, ancak analiz yalnızca ilk 8192 bayt üzerinde
yapılır ve eşik dolur doldurulmaz — daha fazla bayt okunmaz:
libsyswin/src/filedataprovider.cpp (1195-1210) — birebirSADECE İLK 8 KiB
// Content-based ASCII-text check. Exact C++ port of:// fn looks_like_ascii_text(data: &[u8]) -> bool// Empty input -> false. Samples at most the first 8192 bytes, fail-fast once// the non-printable count reaches 15% + 1. Printable set: \t \n \r 0x20..=0x7E.bool FileDataProvider::is_look_like_ascii_text(const uint8_t* data, size_t len)
{
if (data == nullptr || len == 0) returnfalse;
const size_t sampleLen = len < 8192 ? len : 8192; // <-- ANALİZ PENCERESİ 8 KiBconst size_t threshold = sampleLen * 15 / 100 + 1; // 8192'de = 1229 bayt
size_t nonPrintable = 0;
for (size_t i = 0; i < sampleLen; ++i)
{
const uint8_t b = data[i];
const bool printable = (b == '\t' || b == '\n' || b == '\r' || (b >= 0x20 && b <= 0x7E));
if (!printable && ++nonPrintable >= threshold)
returnfalse; // <-- FAIL-FAST
}
returntrue;
}
Akış (1217-1236).isAsciiTextFile() → dosya nesnesinde deleted işareti varsa
disk hiç okunmadanfalse → getFileStream() ile
dosya Read | ShareRead | ShareWrite modunda açılır (gerekirse süreç token'ı ile
impersonation denenerek ikinci kez açılır) → 64 KiB'a kadar
offset 0'dan tek seferde okunur → okunan bayt 0 ise false →
yukarıdaki 8 KiB'lık analiz.
ShareWrite bayrağı sayesinde dosya, saldırganın o anda açık
yazma tutamaçlarıyla birlikte okunabilir — yani şifreleme sürerken dosya
kilitli değildir ve tarama yine içeriği görür.
Neden 8 KiB yeterli? Bir düz metin dosyasının ilk 8 KiB'i zaten
yazdırılabilir karakterlerden oluşur; şifre metninde ise 1229 bayta ulaşmak (~15%)
rastgele dağılımda neredeyse garanti edilir. Bu, maliyeti 64 KiB
tarama yerine 8 KiB + fail-fast'e indirir.
Neden bu kadar belirleyici? Bir JPEG, bir MP4, bir .docx
(ZIP), bir .xlsx, bir veritabanı dosyası — hepsi metin gibi
görünür ama asla metin değildir. Kural yalnızca açıkça metin olan
dosyaları eler. Bu, “şifreleme yapmış gibi görünen ama aslında sıkıştırılmış bir
klasör kopyalama işlemi” ile “gerçek şifreleme” arasındaki ayrımı kurar.
Fail-closed davranışı: dosya silinmiş, açılamıyor ya da 0 bayt okunduysa
sonuç false— yani “metin değil”— döner. Kural, kanıt
toplayamazsa sert davranır ve dosyayı elmez. Bu bilinçli bir tercihtir:
yanlış negatif (kaçırılan gerçek şifreleme) maliyeti, yanlış pozitif (kullanıcının
meşru dosyasının karantinaya alınması) maliyetinden daha yüksektir.
Tetikleyiciye göre ince ayrıntı:LLE_FILE_CREATE üzerinde
dosya henüz boş olduğundan 0 bayt okunur ve süzgeç geçer;
LLE_FILE_RENAME / LLE_FILE_DELETE üzerinde ise içerik
o âna kadarki hâlidir — henüz şifrelenmemiş bir dosyada süzgeç eleyebilir
(LLE_FILE_DELETE'de ayrıca deleted işareti okuma yapmadan
false döndürür). Kuralın asıl tetikleyicisi LLE_FILE_DATA_WRITE_FULL
olduğu için bu, normal şifreleme akışında bir sorun yaratmaz.
Kuralın can damarı. Diğer sekiz süzgeç tek başına bir dosya işlemini
tanımlar; bu süzgeç onu bir saldırıya çevirir. 60 saniye içinde
3 veya daha fazla AYRI hedef dosyaya dokunulmuş olması gerekir.
Bkz. Bölüm 8. Tek bir dosyaya yapılan işlem, ne kadar şüpheli olursa olsun
asla tetiklemez. Bu, “bir kullanıcı bir fotoğrafını düzenledi”
ile “birisi diski şifreliyor” arasındaki farkı kuran şeydir.
08distinctCounter: Kayan Pencerede Ayrı Değer Sayacı
distinctCounter, motorun en özgün yönergesidir. Basit bir sayaç değildir:
aynı değerin tekrarı sayıya katkı yapmaz. Bu küçük ama kritik ayrım,
kuralın neden “3 dosya” dediğini açar.
"distinctCounter": {
"basket": "ransomFileDistinct", // ayrı sayaç alanı (konteyner)
"key": "@event.process.pid", // HER SÜREÇ AYRI SAYILIR
"values": [ "@event.destination.path" ], // sayılan değer: HEDEF YOLU
"threshold": 3, // 3 AYRI hedef
"window": 60000, // 60 saniye kayan pencere
"flag": "@event.RansomThresholdMet"// sonuç buraya yazılır
}
8.1 — Semantik: ne sayıyor, ne saymıyor?
Özellik
Davranış
Fidye Yazılımı Açısından Anlamı
Sayılan
Aynı anahtar içinde birerinden FARKLI, boş olmayan değerler
“Kaç farklı dosyaya dokundu?” — doğru soru
Sayılmayan
Aynı dosyaya tekrar tekrar yazılması
Yedekleme aracının 500 kez aynı dosyayı yazması → sayaç = 1, eşik geçilmez ✓
Anahtar
@event.process.pid — PID bazlı
Süreç izolasyonu: iki farklı zararlı birbirinin sayacını dolduramaz; bir yedekleme aracı sayacı kirletemez
Pencere
60000 ms; her çağrıda süresi dolan kayıtlar seyrek olarak temizlenir
Size >= threshold olduğu sürece true — kenar değil, seviye (latching)
Pencere dolmadan tespit devam eder; her yeni dosya yeniden tetikler
Temizlik
Açık bir sıfırlama yolu yoktur; yalnızca pencere süresi dolar
Bkz. Bölüm 16 — kasıtlı veya kasıtsız bir sonuç
8.2 — Somut Senaryo
PID 4711 — suspect.exe (0. saniye)
yaz C:\Users\alice\Documents\sozlesme.docx -> AYRI #1 sayaç = 1
yaz C:\Users\alice\Photos\tatil.jpg -> AYRI #2 sayaç = 2
yaz C:\Users\alice\Desktop\butce.xlsx -> AYRI #3 RansomThresholdMet = TRUE
|
+--> S1..S8 doğrulanır ==> MLE_RANSOM_BEHAVIOR
|
+--> yaz C:\Users\alice\Videos\dogum.mov
-> AYRI #4 bayrak HALA TRUE
==> ikinci MLE_RANSOM_BEHAVIOR60 saniye sonra: tüm kayıtlar bayatlanır, pencere temizlenir
61. saniye: yeni bir yazma -> sayaç yeniden 1'den başlar
▶ Neden 3 ve neden 60 saniye?
3 eşiği, üç sayıyı birden dengeler: (a) tek dosyaya müdahale etmeyen
meşru yazılımları eler, (b) iki dosyalık örnek fidye yazılımı numunelerini
(test numunelerinde sık görülür) engeller, (c) gerçek şifreleme turlarında dosya
sayısı hızla onlara çıktığı için tespiti ertelemez.
60 saniyelik pencere, “sık kullanıcı eylemi” ile “makine
hızıyla yapılan toplu işlem” arasındaki makul zaman farkını yakalar. Bir insan
60 saniyede 3 belgeyi şifrelemez; bir fidye yazılımı saniyeler içinde yüzlerce yapar.
Ticari bir taviz: “yavaş sürünen”, dosya başına birkaç dakika
bekleyen sofistike şifreleyiciler bu pencereyi aşabilir. Bunu yakalayan başka bir
kural değil, kombine edilmiş zamansal davranış olacaktır. Bu, kuralın
bilinen ve dürüstçe belgelenmiş sınırıdır.
09 Dosya Nesnesi: Hangi Alana Bakılıyor?
Kuralın @event.destination.* ve @event.file.* ifadeleri,
sürücüden gelen ham bir yol değil, zenginleştirilmiş bir dosya nesnesinin
alanlarına işaret eder. Bu nesne tembel (lazy) üretilir: bir alan
gerçekten okunmadıkça diskte de okunmaz.
Alan
Kaynağı
Kuralda Kullanımı
path
Normalize edilmiş, insan tarafından okunabilir yol
S1, S2, S3, S4, S5, S7, S9 — yolun kendisi
type
Uzantı yapılandırılmış çalıştırılabilir listesinde mi? (EXECUTABLE / OTHER)
S6 — == "OTHER"
isAsciiText
Diskten 64 KiB okunur (offset 0, ShareRead|ShareWrite, gerekirse impersonation ile); metin analizi yalnızca ilk 8 KiB (8192 bayt) üzerinde yapılır, eşik %15+1 (8 KiB'de 1229 bayt), fail-fast. Okuma anı: kuralın değerlendirildiği andaki güncel içerik — yani şifrelendiyse ciphertext.
S8 — != true
size
Tembel dosya boyutu
Doğrudan kullanılmıyor (kernel zaten tam-okuma / tam-yazma eşiğini uygular)
rawHash
Okuma/yazma dizisi boyunca xxh64
Bu kuralda doğrudan kullanılmıyor; MLE_FILE_COPY kuralında kullanılır
renameTarget
Yalnızca yeniden adlandırma olaylarında yeni ad
Yok — aday olaya taşınmıyor
volume
{guid, type, device} — sabit / çıkarılabilir / ağ
Bu kuralda yok (USB kurallarında kullanılır)
owlyEntropy
Olay kök alanı — yalnızca tam yazma olayında
S7c
▶ Tembel değerlendirmenin güvenlik ve performans değeri
isAsciiText her değerlendirmede diskten okuma demektir. Sistem bunu
tembel bir vekil nesne (lambda proxy) olarak kurar: kural o alana
gerçekten dokunmadıkça hiçbir bayt okunmaz. 10 kernel tetikleyicisinden
hiçbiri bu alanı okumuyorsa, tek bir bayt harcanmaz.
Dokunulduğunda ise iş çift katmanlı olarak ucuzdur: 64 KiB'lık I/O
tek çağrıda yapılır, ama analiz ilk 8 KiB'de fail-fast tamamlanır.
Yani tipik şifre metninde karar yaklaşık 1229 bayt okunduktan sonra
verilir — kalan 63 KiB hiç taranmaz. Bu, bir EDR'ın canlı sistemde
kullanılabilir olmasının temelidir.
10 Shannon Entropisi: Q24 Sabit Nokta ile Hesaplama
Entropi, verinin ne kadar “rastgele” olduğunun ölçüsüdür. Şifreli veri
(AES, ChaCha) teorik maksimuma, yani 8 bit/byte'a yakın değerdir.
Düz metin ise 3,5–5 bandındadır. Bu fark, “şifreli mi?” sorusunu
sayısal bir ölçüte çevirir.
10.1 — Algoritma
edrdrv/src/ShanonEntropy.cpp + ShanonEntropy.hRING-0 — FLOAT YOK
Bu hesap Ring-0'da, her yazma işleminin post-operasyonunda çalışır.
Kayan nokta işlemleri (SSE/MMX) Windows çekirdeğinde kesin olarak yasaktır
— bir istisna, işlem iptaline ve sistem kararsızlığına yol açar.
Bu yüzden log2 fonksiyonu atanh serisiyle tam sayı
aritmetiğinde yeniden yazılmıştır (/3, /5, /7, /9, /11, /13 terimleri),
24 kesirli bit (Q24) kullanılır ve sonuç sert biçimde
8 × 2^24 değerine kırpılır. Hassasiyet yaklaşık
6×10^-8 bit/byte'dır — bu, karar eşiği olan “6” için
fazlasıyla yeterlidir.
10.3 — Nerede ve nasıl örneklenir?
Özellik
Değer
Örnekleme yeri
Yalnızca yazma işlemi (pre/post write, her MDL sayfası ve her doğrudan tampon)
Özetleme
Dizideki maksimum parça entropisi
Yayın
Yalnızca LLE_FILE_DATA_WRITE_FULL olayında (owlyEntropy + owlyIsEntropyCalc=1)
Tip
uint64 Q24 sabit nokta — ham değer, ölçeklenmemiş
Doğruluk notu
Seri 6 terimde kesildiği ve mantis kesildiği için IEEE kesin değildir; pratik aralıklarda birkaç ULP sapma
10.4 — Tipik değerler
İçerik
Teorik H (bit/byte)
Q24 Ham Değer
Kuralda Sonuç
Tamamen sabit (sıfırlar)
0,0
0
ELE
İngilizce metin, kaynak kodu, .txt/.js/.html/.json
3,5 – 5,0
58.720.256 – 83.886.080
S8 ile elenir
Derlenmiş PE (kod + veri karışımı)
6,0 – 7,2
100.663.296 – 120.795.955
GEÇER
Sıkıştırılmış arşiv (.zip/.7z/.zst)
7,9 – 8,0
≈132.120.000 – 134.217.728
GEÇER
Taze AES/ChaCha şifre metni
7,9 – 8,0
≈132.120.000 – 134.217.728
GEÇER
▶ Ölçekleme kusuru (bilinen sınır)
Sürücü entropiyi ham Q24 tam sayısı olarak yazar (0 … 134.217.728),
ancak politika kuralı bunu 6 ile karşılaştırır — yani
ham Q24 ile karşılaştırır. Bu, “3,6×10^-7
bit/byte'den büyükse geç” anlamına gelir; yani pratikte “bu olay bir tam yazma
olayı mıydı?” sorusuna dönüşür.
Ölçekleme filemon.cpp yorumunda (“Rust tarafı 1<<24 ile böler”)
belirtilmiştir ve bu, OwlyShield Rust tüketicisi için geçerlidir;
PTM/C++ yolunda ölçekleme yapılmaz. Sonuç olarak gerçekten çalışan entropi
ayrımı 8. süzgeçtir (isAsciiText), 7c bloğu ise “tam yazma
olayı mıydı” filtresi olarak işlev görür. Ayrıntı ve önerilen düzeltme:
Bölüm 16.
11 Beyaz Liste ve Uzantı Gözcüsü
11.1 — Sistem İstisnaları (8 kayıt)
Bu liste, kuralın en sıkı çalışan iki süzgecini besler (S1 ve S2). Yorum satırı kaynakta
açıkça belirtilmiştir: “System and browser cache paths excluded from ransomware
tripping (FP reduction)” — yanlış pozitif azaltma amacıyla sistem ve
tarayıcı önbellek yolları hariç tutuldu.
// System and browser cache paths excluded from ransomware tripping (FP reduction)"ransomSystemExclusions": [
"*\\Windows\\*", // mod "all" <-- içinde \Windows\"C:\\Windows\\explorer.exe", // mod "begin" <-- İLE BAŞLIYOR"*\\Program Files\\*", // mod "all""*\\Program Files (x86)\\*", // mod "all""*\\ProgramData\\*", // mod "all""*\\AppData\\*", // mod "all" (harf duyarsız)"*\\Temp\\*", // mod "all""*:Zone.Identifier*"// mod "all" <-- NTFS ADS
],
▶ C:\Windows\explorer.exe satırının ince ayrıntısı
Bu kalıp joker içermediği için önek (begin) eşlemesine dönüşür.
Yani C:\Windows\explorer.exe ile başlayan her yol eşleşir
— C:\Windows\explorer.exe.bak gibi. Bu, bilinçli bir genişliktir: kurucu
masaüstü yedekleme aracı explorer.exe.bak oluşturduğunda da elenir.
Aynı şekilde *\Windows\* zaten C:\Windows\ altını kapsar; bu
satır redundanttır ama explorer.exe adı özellikle
belirtildiği için, sonradan \Windows\ kalıbının daraltılması hâlinde
korunabilir bir yedek olarak bırakılmış görünür.
Kaynak yorumu şunu söyler: “Extensions of user files whose encryption is treated
as ransomware-relevant. Scopes the file-encryption candidate so extensionless / temp files
do not FP.”
Yüksek değerli “özgün emek” dosyaları en çok kaybedilen tür
Medya / yaratıcı
*.blend *.psd *.indd *.cdr *.aep *.prproj *.raw
Fotoğraf ve video arşivleri — genelde en çok foto olan dizin
Yapısal olarak geniş girişler
*.0 *.1 ... *.100, *.log *.tmp *.dat
Düşük sinyal ama süzgeç 7a & 7b ile birleşince etkisiz
▶ Listeyi daraltan tasarım: iki katmanlı koruma
ransomFileExtensions bilinçli olarak geniş bir listeye
dayanır — 1479 uzantı, tek bir StringMatcher tablosunda neredeyse maliyetsizdir.
Daraltma, sonraki süzgece bırakılmıştır (7a: nokta var mı; S8: metin mi
değil mi; S9: 3+ ayrı hedef). Bu, bir listeyi devamlı güncel tutma yükünü
kaldırırken tespit gücünü korumanın doğru mimarisidir: geniş aday havuzu +
katlı sert filtreler, dar ve güncellenmeye muhtaç bir aday havuzundan iyidir.
12 Kernel Tarafı: Hangi IRP, Hangi Olay?
Kuralın tetikleyicilerinin tamamı Ring-0'da üretilir. Minifilter, yedi IRP major
fonksiyonunu kaydeder. Bu kayıt, kuralın tamamını belirler:
“ Aç dosya, sonra yaz” ile “oluştur ve yaz” ayırt edilir
Yedekleme adı istisnası
Yolu içeren HydraDragonBackups giren dosyalar atlanır
Kendi geri yükleme mekanizması kendi kendini tetiklemez — önemli bir önlem
▶ Önemli: bu teknik, BYOVD ve anti-EDR atlatmaya dayanık
Biçim yazılımında yazan zarilı, özgün dosyanın üzerine in-place yazar, çift
dosya üretir ya da yeni dosya oluşturup orijinali siler. Bu üç modelin üçü de
“IRP_MJ_CREATE + WRITE/CLEANUP + SET_INFORMATION” izi bırakır —
API hooking yapmadan yakalanır. Klasik kancalama tabanlı AV'ler
(usermode) bunu görmez; Ring-0 minifilter görür.
12.3 — mmap yolu: görünmez olanın yakalanması
edrdrv/src/filemon.cpp (2564-2573) — sürücü yorumu, birebirNEDEN AYRI BİR IRP?
/* mmap ile yapılan okuma/yazma HİÇBİR IRP_MJ_READ / IRP_MJ_WRITE
üretmez. NtCreateSection + NtMapViewOfSection doğrudan belleğe
eşler. Bu yüzden tek görünür sinyal, section edinme IRP'sidir. */
Sürücü, PAGE_READONLY korumasını okuma niyeti, PAGE_READWRITE /
PAGE_WRITECOPY / PAGE_EXECUTE_READWRITE / PAGE_EXECUTE_WRITECOPY
korumalarını yazma niyeti olarak sınıflandırır. accessMask alanına sayfa
koruması yazılır.
▶ Modern şifreleyicilerin tercihi
Rust (özgür yazılım dili), Go ve C++ ile yazılan profesyonel fidye yazılımlarının belirgin bir
bölümü dosyaları mmap ile işler — çünkü bu yol daha hızlıdır
ve kullanıcı modundaki API kancalarına iz bırakmaz. LLE_FILE_MAP_READ ve
LLE_FILE_MAP_WRITE maddeleri olmasa, bu sınıf fidye yazılımı kurala tamamen
görünmez olurdu.
13 Neden İki Ara Olay Var?
MLE_RANSOM_CANDIDATE ve MLE_RANSOM_EVALUATE ara olayları
“gereksiz işlem” gibi görünebilir. Değildir. Üç somut faydaları vardır:
Fayda
Açıklama
Sözdeşleme (decoupling)
Aşama 2 sadece toplama yapar, aşama 4 sadece karar verir. İki sorumluluk tek yerde birleştirilseydi, eşik hesabı üretim mantığının içine gömülür ve değiştirilemez hâle gelirdi.
Kural izlenebilirliği
Log ve debugger akışında her adım ayrı bir olay adıyla görünür. “Aday üretildi ama eşik tutmadı” ile “Aday bile üretilmedi” ayırt edilebilir — bu, üretimdeki kütüphaneler için kritik bir ayrım yeteneğidir.
Politika genişletilebilirliği
MLE_RANSOM_EVALUATE çıkışı herkese açık bir noktadır. Gelecekte yeni bir kural (örn. “farklı eşik, farklı pencere”) aynı aday akışına bağlanabilir; mevcut kurala hiç dokunulmadan.
▶ Kurumsal mimari dersi
Toplama ile karar ayrımı, büyük olay işleme sistemlerinde standart bir
kalıptır. Kural bu kalibi veritabanında değil, dosya sistemi gözleminde
uygular. Ucuz, denetlenebilir ve üzerine inşa edilebilir.
14 Yanıt Zinciri: Tespitten Sonrası Ne Olur?
Tespit üretilmesi bir son değildir. MLE_RANSOM_BEHAVIOR olayı
OUT ile zenginleştirme, güven kontrolü, öldürme, karantina ve geri yükleme
zincirinden geçer. Aşağıdaki akış DetectionNotifier::execute("put")
içindeki gerçek sıradır.
MLE_RANSOM_BEHAVIOR (baseType 1000008, OUT)
|
|--> [1] severity = 3 (CRITICAL) + alert_kind = "critical" <-- her tespitte sabit
|
|--> [2] Başlık sentezi + quarantineTarget kilidi
| <- @event.process.imageFile.rawPath (SALDIRGANIN İKİLİSİ)
|
|--> [3] OwlyShield verdict senkronizasyonu (owlyshield_update_process_verdict)
|
|--> [4] GÜVEN KONTROLÜ (aşağıda ayrıntılı)
| should_trust_company_whitelist = true
| should_trust_comodo_fls_cloud = true
| -> güvenilir imzalı / FLS SAFE ==> öldürme + karantina İPTAL
| -> kötü / PUA üreticisi ==> sıkı uygulama (strict enforcement)
|
|--> [5] SÜRECİ ÖLDÜR — edrdrv IOCTL
| DeviceIoControl(\\.\\{157980D8-09B4-4580-B8B6-D32971D056DA})
| msgType = 6 (MESSAGE_KILL_ONLY_GID), hedef = Global ID
|
|--> [6] KARANTİNA — owlyshield_ransom.dll
| owlyshield_dll_quarantine_file(dosyaYolu)
| + recordMalwareDetection() (kalıcı veritabanı kaydı)
| hata hâlinde: \\.\pipe\HydraHipEvent üzerinden
| "Malicious file locked! Please restart your computer..."
|
|--> [7] GERİ YÜKLE — EventEnricher::rollbackRansomBackups()
| Süreç, kök süreç, sadece GID — üçü için ön-image geri yükleme
| (RansomShield'in %PROGRAMDATA%\HydraDragonBackups altına
| kaydettiği özgün kopyalar)
|
+--> [8] GUI bildirimi + 1000 kayıtlı halka tamponuna ekleme
14.1 — Neden quarantineTarget sürecin imajıdır?
▶ Kritik mimari karar
Aday aşamasında quarantineTargetşifrelenen dosyayı işaret
eder. Nihai tespitte ise sürecin kendi imaj dosyası atanır.
Bu, kuralın en büyük ayırt edici özelliğidir. Klasik EDR'lar genellikle
mağdur dosyayı karantinaya alır — bu, kullanıcının verisini
korumaz, sadece zararlı dosyayı uzaklaştırır. Viruskov ise
aracı yok eder, veriyi geri yükler. İkisi arasındaki fark, bir ürünün
“tespit eden” mi yoksa “kurtaran” mi olduğudur.
14.2 — Güven bayrakları: should_trust_*
Bayrak
Değer
Etkisi
should_trust_company_whitelist
true
owlyshield_is_trusted_company_signer çağrısına izin verir. Güvenilir kurum imzalı bir ikili → öldürme + karantina iptal edilir (alarm kaydı yine tutulur).
should_trust_comodo_fls_cloud
true
FLS bulutu verdict = 1 (SAFE) dönerse veto verir. Ayrıca verdict = 4 (FAIL/ERROR) durumunda da veto verir — böylece bulut kesintisi tespit sistemini felç etmez.
owlyshield_is_malicious_company_signer
güven bayraklarını geçersiz kılar
Kötü / PUA üreticisi tespit edilirse verdict = 2'ye zorlanır ve sıkı uygulama devreye girer; beyaz liste devre dışı kalır.
▶ Bu seçim bilinçli ve doğru
Fidye yazılımının “çalıştırılan dosyası” çoğu zaman imzasız bir ara binary
olabilir (LiveWire, PowerShell, script host, imza çalıntısı kod). Bu yüzden
should_trust_* bayrakları bu kuralda açık tutulmuştur
(false değil). Bu, kurumsal güvenlikte “imzalı = güvenli”
varsayımının doğru tarafta durmasını sağlar. Aynı anda
“güvenilir imzalı → şifreleme yapan exe” senaryosu teknik olarak imkânsıza
yakın olduğu için bu ayar pratikte FP üretmez, yalnızca imzalı ancak davranışı düşürülmüş
(supply-chain) vakaları doğru muameleye alır.
▶ Zararsızlaştırmada yapılmayanlar
put yolunda ağ engelleme (firewall / IPC block),
bootkit / sürücü temizliği veya buluta MLE gönderimiyapılmaz. Bunlar ayrı modüllerin alanıdır. MLE_RANSOM_BEHAVIOR
tek işine odaklanır: saldırganı durdur, veriyi geri getir, analiste bildir.
15 Neden Mükemmel Bir Ransomware Kuralı?
“Mükemmel” burada şu anlama geliyor: sıfırıncı gün farkından
bağımsız, hedef odaklı, kanıt temelli, ve kanıtlanabilir olarak kurtarıcı.
Aşağıda on iki somut gerekçe.
R01 • BAĞIMSIZLIK
Koddan tamamen bağımsız
Kural, dosyanın içeriğine, hash'ine, derleyicisine, paketleyicisine ve boyutuna bile bakmaz. Zararlı kodu tamamen değiştirilse, yeniden paketlense, yeni bir dilde yeniden yazılsa — kural aynen çalışır. Bu, tek başına sıfırıncı gün kapsamıdır.
R02 • ZORUNLULUK
Şifreleme zorunlu bir adımdır
Fidye yazılımının amacını yerine getirmesi için dosyayı geri döndürülemez şekilde bozması şarttır. “Yok et", “şifrele", “uzaktan sil" — üçü de aynı dosya sistemi izini bırakır. Bu, kuralı zorunlu bir davranışa bağlar: atlanması mümkün değildir.
R03 • ADLIMSIZ
Şifreleme çift taraflıdır
Aile, varyant, derleme tarihi, dil, paketleyici — hiçbiri önemsizdir. Kural “bu bir LockBit” değil, “bu bir şifreleme” der. Bu yüzden yeni varyantlar ayrı iş değildir.
R04 • RING-0
Çekirdekte gözlem
Tespit usermode kancalarına değil, IRP'lere bakar. Bu, iki kritik avantaj sağlar: (a) usermode API kaçakları çalışmaz, (b) BYOVD ile antivirüsün bellekten silinmesi tespiti engellemez — motor çoktan çalışıyordur.
R05 • MMAP
Görünmeyi izlemeyi yakalar
NtMapViewOfSection hiçbir IRP_MJ_READ/WRITE üretmez. LLE_FILE_MAP_READ / MAP_WRITE maddeleri sayesinde haritalama tabanlı şifreleyiciler de yakalanır. Birçok EDR bu yolu tamamen kaçar.
R06 • KÜTLE
Tek işlem yeterli değil
60 saniyede 3 ayrı hedef eşiği, tek dosya işlemlerini tamamen devre dışı bırakır. Bu, kuralın en önemli FP güvenlik ağıdır: “bir kullanıcı bir dosyasıyla uğraştı” ile “birisi toplu şifreliyor” birbirinden matematiksel olarak ayrılır.
R07 • AYRIK SAYIM
Tekrarlar sayılmaz
distinctCounterayrı değer sayar. 500 kez aynı dosyaya yazan bir yedekleme aracı sayacı dolduramaz. Bu, “çok sayıda işlem" ile “çok sayıda farklı dosya" arasındaki kritik ayrımı yapar.
R08 • SÜREÇ İZOLASYONU
Anahtar = PID
Sayaç süreç başına tutulur. İki farklı uygulama birbirinin sayacını dolduramaz; bir tarayıcı eklentisi başka bir sürecin başarısını “miras" alamaz. Bu, kurumsal ortamda yanlış pozitif zincirini kırar.
R09 • İÇERİK KANITI
İçerik, dosya adına değil
isAsciiText dosyanın gerçek içeriğini ölçer: diskten okunan ilk 8 KiB'da %15+1 yazdırılamaz bayt arar. “Zararlı, çünkü adında .doc vardı" değil, “zararlı, çünkü bu artık düz metin değil" der. Uzantılara göre karar vermek, kurumsal e-posta şifreleme ve belge işleme yazılımlarını sürekli yanlış pozitife sokardı.
R10 • KONTROL MEKANİZMASI
Tembel değerlendirme
İçerik taraması yalnızca kural o alana gerçekten dokunduğunda çalışır. Dokunulduğunda 64 KiB'lık I/O yapılır ama karar ilk 8 KiB'de fail-fast verilir. Aynı kural, hem canlı bir sistemde kullanılabilir hem de ispatlanabilir bir FP oranına sahiptir. “Sıkışık ama yavaş" değil.
R11 • KURTARMA
Sadece tespit değil, kurtarma
Karantina saldırganın ikilisine uygulanır, kullanıcının verisi geri yüklenir (RansomShield ön-image). Tespit edilen ama verisi kaybedilen bir sistem, kurtarılmış sayılmaz. Bu kural, tespit ile eylemi birleştirir.
R12 • DENETLENEBİLİRLİK
Kapalı kutu değil, okunabilir politika
Kural JSON'da, 16 madde hâlinde, her hâliyle duruyor. Şu an okuduğunuz her şey ürünün içinde birebir var. “Neden bu alarmı attı?” sorusunun cevabı bir destek talebine değil, bir ekran görüntüsüne gelir. Bu, projenin temel felsefesidir: Security by Obscurity değil, Security by Legibility.
15.1 — Saldırgana karşı bu kuralı nasıl atlamaya çalışırsınız?
Güvenli tarafı test etmek için, bir red team sorusunu cevaplamak gerekir:
Saldırgan Tekniği
Kurala Etkisi
Neden
Paketleme / polimorfik kod
ETKİSİZ
Kural koda değil, dosya sistemi işlemine bakar
API kancalama ile ReadFile/WriteFile atlama
ETKİSİZ
IRP katmanı gözleniyor; usermode API görünmez değil, ilgisiz
Doğrudan NtWriteFile / ZwWriteFile
ETKİSİZ
Aynı IRP'ye iner
Yerleşik dosya sistemi API'leri (SetFileInformationByHandle ile silme)
ETKİSİZ
IRP_MJ_SET_INFORMATION izlenir
Dosya sistemi filtresi (minifilter) ile öndenleme
ZOR — kötü niyetli
Çalışan bir “meşru" minifilter görünür; o da tanımlanması gereken ayrı bir yazılımdır
WMI / uzak yönetim ile toplu şifreleme
ETKİSİZ
Yine aynı dosya sistemi işlemi
Sadece C:\ kökünü şifreleme (kullanıcı dışı)
KAPSAM DIŞI
S3 süzgeci \users\ zorunlu kılıyor — bu bilinçli bir sınırdır (bkz. Bölüm 16)
Dosya başına birkaç dakika bekleyerek yavaş şifreleme
KAÇIRILIR
60 saniyelik pencere aşılır — bilinen sınır (bkz. Bölüm 16)
Önce AppData altına yazıp oradan taşıma
KAPSAM DIŞI
S2 & S4 \AppData\'yi tamamen eler
Yedekleri sil, sonra şifrele
ETKİSİZ
Komşu kural MLE_RANSOM_SHADOW_COPY_DELETE devreye girer (Bölüm 17)
Korunan: İmza değişimi, paketleme, API kaçakları, kod tamamen yeniden
yazımı, mmap, yerleşik API'ler, yeni dil, yeni derleyici, uzaktan çalıştırma. Aşılır: Yavaş şifreleme, disk kökü şifreleme, salt metin hedefleme. Bu, ticari bir trade-off. Her EDR bu dengeyi yapar; Viruskov bunu
gizlemez, tam olarak bu sayfada olduğu gibi belgeler.
16 Dürüst Sınırlar ve Bilinen Kusurlar
Bu bölüm bilinçli olarak yazılmıştır. Bir ürünün güçlü yanlarını anlatmak kolaydır;
zayıf yanlarını aynı sayfada, aynı cesaretle anlatmak ise güveni
insani kılar. Aşağıdaki maddeler, kaynak kod incelemesiyle tespit edilmiş
gerçek bulgulardır — ne varsayım, ne kötü niyet, sadece açık gerçek.
16.1 — Tasarımdan kaynaklanan sınırlar
#
Sınır
Etki
Düzeltme Yolu
1
60 saniyelik pencere dosya başına birkaç dakika bekleyen sofistike şifreleyicileri kaçırır
Orta — yüksek hacimli her şifreleme yine yakalanır
Pencereyi 5-10 dakikaya çıkarmak, eşiği 5-10'a çıkarmak veya zamansal oran (dosya/sn) metriği eklemek
2
S3 *\users\* zorunluluğu disk kökünü veya ağ sürücüsünü şifreleyenleri kapsam dışı bırakır
Düşük — bu hedefler genelde kullanıcı verisi değildir
Yeni bir kural: MLE_RANSOM_BEHAVIOR_SYSTEM (yol bağımsız, C:\Windows hariç)
3
Yalnızca tam yazma olaylarında entropi ölçülüyor — LLE_FILE_MAP_WRITE için entropi üretilmiyor
Düşük — S8 isAsciiText bu yolu zaten kapatıyor
preAcquireForSectionSync içinde mmap sayfası örneklemesi
4
S4, S2 ile aynı kontrol (çift denetim)
Yok — savunma derinliği, maliyeti sıfır
Korunması önerilir; okunabilirlik ve örnek olma değeri yüksek
5
ransomFileExtensions çok geniş (sayısal ve kısa uzantılar, *.0 … *.100)
Düşük — 7a + S8 + S9 birlikte filtreliyor
Listeyi gerçek kurban uzantılarına daraltmak. Not: 165. satırdaki "*.2538)" kaydı bir yazım hatasıdır.
16.2 — Kodda doğrudan doğrulanmış kusurlar
▶ KUSUR 1 — Üç kural maddesi birebir tekrar ediyor
LLE_FILE_RENAME (2f), LLE_FILE_DELETE (2g) ve
RF4* (2h) maddeleri — her biri iki kez yazılmış.
Toplam 16 maddenin 3'ü gereksiz.
Etki:Yok. Aynı olay için aynı
MLE_RANSOM_CANDIDATE üretilir; sayaç ayrı değer saydığı için ikinci
üretim sayacı değiştirmez. Ancak politikanın okunabilirliğini zarlar
ve ileride biri düzeltilirse diğeri değişmiş olur.
Düzeltme: 2f', 2g' ve 2h' maddelerini silin. 16 madde → 13 madde.
▶ KUSUR 2 — RF10* ve RF4* derlemede düşer
Politika derleyicisi, joker içeren eventType anahtarlarını
önek eşlemesiyle genişletir ve yalnızca aynı politikada
jokersiz olarak geçen olay tiplerine bakar. RF10* için önek
"RF10", RF4* için "RF4" — ikisi de
sıfır eşleşme üretir, anahtar silinir ve kural sessizce
kaybolur.
Temel neden:RF* takma adları (v1 / eski EVM motorunun
olay sınıf adları: RF4 = LLE_FILE_DELETE, RF10 = LLE_FILE_DATA_CHANGE)
v2 motorunda tanımlı değildir. Doğru kullanılmaları gereken isimler
LLE_FILE_DELETE ve LLE_FILE_DATA_CHANGE'dir.
Etki:Fonksiyonel kayıp yok. Kastedilen
iki olay (LLE_FILE_DATA_CHANGE = madde 2d, LLE_FILE_DELETE =
madde 2g) zaten doğrudal olarak kapsanmış. Geriye yalnızca derlenmiş
çıktıda üç kuralın kaybolması ve derleyicinin uyarı üretmemesi kalıyor.
Düzeltme: 2e, 2h ve 2h' maddelerini tamamen silin; kuralın mantığını
değiştirmez.
▶ KUSUR 3 — Okuma sepeti yazılıyor ama okunmuyor
Aşama 1a ve 1b, saveContext ile RANSOM_FILE_READ_WRITE
sepetine kayıt yazar. Ancak RANSOM_BEHAVIOR içinde bu sepeti okuyan
hiçbir loadContext yoktur. Aşama 2 yalnızca LLE_FILE_CREATE
ve yazma/haritalama/değiştirme/rename/delete olaylarını tetikler —
okuma olayları tetikleyici değildir.
Etki:Kuralı etkilemez (eşik kararı
tamamen yazma tarafındadır). Yalnızca 120 saniye boyunca gereksiz bir bellek
harcanır. Aşama 1 tamamen yasal ve gelişim için hazır bir temel
olmaktadır: “okuyan → yazan” korelasyonu kurmak isteyen bir kural
buraya bağlanabilir.
Düzeltme seçenekleri: (a) Aşama 1'i kaldır, (b) bir
loadContext ile “önce bu dosya okundu mu?” kontrolü ekle
(“fiziksel kopyalama + silme” modeli için gerekli), (c) olduğu gibi bırak.
▶ KUSUR 4 — Aşama 5 temizliği eşleşmiyor
Aşama 1 kayıtları group alanı olmadan yazılır; aşama 5 ise
group: "@event.process.id" ile silmeyi dener. Motor, kayıtları
boş grup işaretiyle sakladığı için grup bazlı silme hiçbir şeyi
bulamaz.
Etki:İşlevsel değil — 120 saniyelik
TTL zaten bu işi doğru şekilde yapmaktadır. Yalnızca süreç daha erken bitse kayıt
gereksizce 120 saniye bekler.
Düzeltme: Aşama 1a/1b'ye "group": "@event.process.id"
eklemek.
▶ KUSUR 5 — Entropi ölçeklenmemiş
Sürücü entropiyi ham Q24 tam sayısı yazar; politika ise onu
6 ile karşılaştırır. Böylece owlyEntropy > 6, pratikte
“değer 6'dan büyük mü?” değil, “değer neredeyse sıfırdan büyük mü?”
sorusuna dönüşür.
Ayrıca şu dört dal hiçbir zaman tetiklenmez:
@event.destination.owlyEntropy (şemada yok;
alan olay kökündedir), @event.destination.entropy / @event.entropy
(hiçbir yerde tanımlı değil). Yalnızca @event.owlyEntropy
dalıdır.
Etki:Orta. 7c bloğu pratikte
“bu bir LLE_FILE_DATA_WRITE_FULL olayı mıydı?” filtresine
dönüşür. Gerçek entropi ayrımı 8. süzgece (S8) yüklenmiştir.
Düzeltme: (a) enricher'da owlyEntropy / (1<<24) ile
ondalık dönüşüm yapıp file.entropy alanı üretmek, (b) eşiği Q24 cinsinden
yazmak: greater(@event.owlyEntropy, 100663296) (= 6 × 2^24),
(c) olmayan destination.owlyEntropy / entropy dallarını kaldırmak.
▶ KUSUR 6 — @event.source daima null
source alanı yalnızca MLE_FILE_COPY* olaylarında doldurulur.
MLE_RANSOM_CANDIDATE ise LLE dosya olaylarından üretildiği için
source hiçbir zaman taşınmaz — aday aşamasında
zaten okunmuyor, değerlendirme aşamasında da null kalır.
Etki:Çok düşük. 7b'nin ikinci dalı
(imatch(ransomFileExtensions, @event.source.path)) kalıcı olarak
false döner; 7a'nın ikinci dalı da aynı şekilde. Yani bu iki dal
etkisiz koddır ve kaldırılabilir.
▶ KUSUR 7 — Yorum dosyası ile kod uyuşmuyor
ptm.local.src içindeki aşama 4 yorumu şu ifadeleri içeriyor:
“The entropy > 6.8 clause...” ve
“plain explorer copies of text .js must never reach the
counter”.
Her iki ifade de yanlıştır: (1) kodda eşik 6,
6.8 değil; (2) sayaç condition'den önce
çalıştığı için metin kopyalamaları sayacı besler (kuralın kendi süzgeci onları
eler, ama sayaç onları görür).
Etki:Yok — yorum yanıltıcı olduğu için
etkilidir. Düzeltme: Yorumu gerçek kodla hizalamak.
16.3 — Derleme ve üretim zinciri notları
Geliştirici derleme aracı:edrdata/compile_ptm.bat,
edrcon.exe compile -o ptm.act common.src ptm.local.src. Üretilen
ptm.act, srcBinInstall ve srcBinUpgrade
dizinlerine senkronize edilir.
Ürün kendisi de derler: Servis başlangıcında politika yeniden
derlenir; geliştirici betiği yalnızca kolaylıktır.
Sınır testi:distinctCounter için yalnızca eşik ve
pencere belirlenir; eşik, pencere, anahtar, sayılan değer tamamen
politika dosyasından gelir — motorda tek bir kurala özel sabit yoktur.
▶ Bu sayfa neden var?
Bir EDR'in kalitesi, hata oranından çok, hatalarını ne kadar hızlı ve dürüstçe
kabul ettiğiyle ölçülür. Yukarıdaki yedi kusur, kapatılmamış olabilir;
ama bilinmektedir. Bu, projenin “Security by Obscurity yerine
Security by Legibility” felsefesinin somut uygulamasıdır.
17 Komşu Kurallar: Tek Başına Yaşamaz
MLE_RANSOM_BEHAVIOR, fidye yazılımı savunmasının tek parçasıdır. Aşağıdaki komşu
kurallar, ona birlikte bakan ve birbirini tamamlayan katmanlardır.
Kural / Olay
baseType
Görevi
İlişki
MLE_RANSOM_SHADOW_COPY_DELETE
1000009
Gölge kopya / yedekleme yok etme komutlarını izler
Önce gelir. Fidye yazılımı şifrelemeden önce yedekleri siler. Bu kural yakalarsa veri kaybı olmaz.
RANSOM_BEHAVIOR
1000008
Bu sayfanın konusu
Ana tespit
CRYPTO_API_MASS
—
Kripto API kullanımını ölçer (CryptEncrypt vb.)
Tamamlayıcı. Dosyaya dokunmadan önce kriptografik iş yapan zararlılar için erken sinyal.
RANSOM_COUNTER_INIT
—
Süreç başına işlem sayacı başlatır (TTL 24 saat)
Temel. Gelecekte süreç başına toplam işlem limitleri için.
LLE_FILE_PREIMAGE_SAVED / RansomShield
0x0037
Şifrelemeden önce özgün dosyanın yedeğini alır
Bu, verinin neden kurtarıldığının cevabıdır. MLE tetiklendiğinde rollbackRansomBackups bu kopyaları geri yükler.
MLE_FILE_COPY*
1000001+
USB / ağ kopyalama tespiti
Dengel. Exfiltration (çıktı) için. 1000008 iç (destruction) ile tamamlanır.
MLE_DRIVER_INSTALL_TAMPER
1000021
Sürücü / özel muafiyet manipülasyonu
Ön muhafız. “Kendini devre dışı bırak” girişimini engeller.
MLE_FLS_MALICIOUS_VERDICT
1000010
Bulut FLS verdict = MALWARE
Ortak güven kapısı. Aynı should_trust_comodo_fls_cloud mekanizmasını kullanır.
1.vssadmin delete shadows → MLE_RANSOM_SHADOW_COPY_DELETE 2.CryptEncrypt çağrıları → CRYPTO_API_MASS 3. Kullanıcı dosyaları okunur → RansomShield ön-image yedek alır 4. Dosyalar şifrelenir → MLE_RANSOM_BEHAVIOR (3. dosyada tetikler) 5. Süreç öldürülür, ikili karantinaya alınır 6.rollbackRansomBackups ile veri geri yüklenir
18 Analist El Kitabı
Bir SOC analisti veya bir güvenlik araştırmacı bu kuraldan nasıl faydalanır?
Aşağıdaki yol, doğru tarafı okumanın en hızlı yoludur.
18.1 — Tespit edildiğinde ilk bakacağınız 8 alan
Alan
Sorulacak Soru
Kritik Değer
quarantineTarget
Hangi ikili karantinaya alındı?
Dosya adı, yolu, imzası
process.id / process.pid
Global ID nedir? (kill hedefi)
Önemli — analiz izinde kullanılır
process.cmdLine
Nasıl başlatıldı?
powershell -enc, mshta, wmic ipuçları
process.imageFile.rawPath
Nerede duruyordu?
%APPDATA%, %TEMP% = yüksek öncelik
destination.path
Hangi dosyaya dokundu?
Son vurulan hedef (tek dosya, < 3 ise eşik tutmadı)
destination.abstractPath
Hedefin “mantıksal” adı ne?
%appdata% gibi belirteçleri çıkar; GUI'de daha okunur
processes[]
Kim bu süreci başlattı?
Üst zincir — ilk bulunan güvenilmeyen ataya kadar in
time / tickTime
Zaman çizelgesi nasıl?
İlk olay, 3. dosya, şifreleme süresi
18.2 — Yanlış pozitif triage kararı
Görünen
Değerlendirme
Karar
Süreç C:\Windows\ veya Program Files altında
S1 zaten elemisti — muhtemelen farklı bir alarm
Triage etmeyin
Hedef yollar AppData altında
S2/S4 elemisti — muhtemelen cache/uygulama işlemi
Triage etmeyin
Hedefler C:\Users\ altında, metin dosyaları
S8 bırakmış olabilir — belge düzenleyici / eşitleme aracı olabilir
Süreç imzası ve cmdLine'a bakın
Süreç güvenilir kurum imzalı, verdict SAFE
Güven kapısı kuralı zaten uyguladı (öldürme olmamalı)
Entegrasyon testi olabilir; kaydedin
Süreç imzasız, %TEMP%'de, işlem sayacı yüksek
Klasik ilk çalıştırma profili
Yüksek öncelik — geri yükleme doğrulayın
processes[] zincirinde bir winword.exe / excel.exe görünüyor
Belge makrosu veya COM hijacking olabilir
Yüksek öncelik; makro analizi yapın
18.3 — Kuralı değiştirmek istiyorsanız
Kural hariciden değiştirilebilir değildir; kaynak kod modifikasyonları
güvenlik açığı riski taşır. En temiz yol, politika dosyasını fork edip
edrdata/ptm.local.src içinde RANSOM_BEHAVIOR bloğunu
düzenlemektir. En sık ihtiyaç duyulan değişiklikler:
SIK KULLANILACAK AYAR NOKTALARI4 SATIR
// 1) Hassasiyet / tolerans ayarı (varsayılan 3/60s)
"threshold": 3, // 2 yaparsanız test örnekleri de yakalanır
"window": 60000, // 300000 yaparsanız yavaş şifreleyiciler de yakalanır// 2) Coğrafya ayarı (zorunlu pozitif koşul)
"pattern": "*\\users\\*" // ağ sürücüleri için ayrı bir kural açın// 3) Yeni korunacak yol eklemek"ransomSystemExclusions": [ ..., "*\\OneDrive\\*", "*\\Google\\Drive\\*" ]
▶ Uyarı
ransomSystemExclusions listesine geniş bir yol eklemek
(*\OneDrive\* gibi) o klasördeki gerçek fidye şifrelemesini
de gizler. Kurumsal bulut eşitleme klasörleri gerçek bir hedef olduğu için,
bu tür genişletmeler dikkatli yapılmalıdır.
19 Sık Sorulan Sorular
01Bu kural bir YARA kuralı mı?
Hayır. Bu bir PTM v2 politika kuralıdır (JSON), C++ motorunda
değerlendirilir. YARA × Viruskov projesinde ayrıca ve birbirini tamamlayıcı
olarak çalışır: YARA dosyanın içeriğine bakar, MLE_RANSOM_BEHAVIOR ise
eyleme. İkisi birlikte hem statik hem dinamik kapsama sahiptir.
02Tek bir dosyayı şifreleyen zararlı yakalanır mı?
Hayır. S9 süzgeci 60 saniyede en az 3 ayrı hedef ister.
Bu bilinçli bir tasarım kararıdır: tek dosya şifrelemek çoğu zaman kullanıcı ya da
yedekleme yazılımı davranışıdır. Kuralı tek dosyada çalıştırmak isteyenler için
threshold: 1 denenebilir — ama yanlış pozitif oranı ciddi şekilde artar.
03Sadece metin dosyalarını şifreleyen zararlı?
Hayır, bu kapsam dışıdır. S8 (!equal(isAsciiText, true))
metin dosyalarını bilerek bırakır. Sebep: kurumsal e-posta şifreleme, belge
işleme yazılımları ve eşitleme istemcileri sürekli olarak metin dosyaları
yazar; bu dosyaları kapsama almak FP oranını kullanılamaz hâle getirirdi.
Bu, bilinçli bir FP/recall trade-off'dur.
04Antivirüsüm zaten var, bu neden gerekli?
Üç somut neden: (1) Ticari AV'ler kullanıcı modunda çalışır;
BYOVD ile bir kerede bellekten silinebilirler — bu kural zaten çalışırken
tespit yapar. (2) Güç veya ağ bağlantısı durumunda beyaz listeler güncellenemez;
bu kural harici imza istemez. (3) Sigorta, denetim ve olay
tespiti (DFIR) için kanıt niteliğinde bir zaman çizelgesi üretir.
05Verilerim ne olacak, gerçekten geri yükleniyor mu?
Evet, dosya sistemi diskinin zaten orijinal verisini koruyorsa.
Minifilter, bir dosyaya in-place yazmadan önce preSetFileInfo
aşamasında orijinal içeriğin ön-image'ini %PROGRAMDATA%\HydraDragonBackups
altına kaydeder. MLE tetiklendiğinde EventEnricher::rollbackRansomBackups()
bu kopyaları geri yükler.
Önemli sınır: Geri yükleme yalnızca ön-image alınabildiği
dosyalarda başarılıdır. Şifreleyici “önce oku, şifreli yeni dosya oluştur, sonra
orijinali sil” modelini kullanırsa, minifilter yine ön-image'i alır
(“her fidye ailesine karşı genel muhafız” yorumu kodda açıkça belirtilir),
ancak diskten tamamen silinmiş veri geri getirilemez.
06Kural güvenilir bir yazılım tarafından tetiklenirse?
İki katmanlı koruma var. (1) S1/S2 beyaz listesi, (2)
should_trust_company_whitelist ve
should_trust_comodo_fls_cloud güven kapısı. İkisi de geçerli ise
süreç öldürülmez ve karantinaya alınmaz; yalnızca alarm kaydedilir. Ancak
owlyshield_is_malicious_company_signer kötü/PUA üreticisi tespit ederse
güven bayrakları geçersiz kılınır ve katı uygulama devreye girer.
07Gerçek bir numune ile örneklenebilir mi?
Evet — YouTube'daki
sıfırıncı gün örneği ile kuralın canlı bir tespit görüntüsü paylaşılmıştır.
Önemli: Fidye yazılımı numunelerini
kendi makinenizde çalıştırmayın. Bilerek bir sisteme bulaştırmak
yasal sonuçlar doğurabilir. Bu projede konteyner / sanal ortam dışında
numune çalıştırmak için teknik destek gerekmez — bütünleşme testi yapmayın.
08Bu kuralı kim, hangi süreç için yazdı?
Kural, OpenEDR (COMODO tarafından açık kaynak yayımlanmış EDR çekirdeği)
fork'u üzerinde, VIRUSKOV / Hydra Dragon projesi kapsamında
geliştirilmiştir. Proje lideri: Emirhan Uçan. Tüm kaynak kod
açık depoda durur;
bu sayfadaki her iddia doğrudan o koddan alınmıştır.
20 Kaynak Haritası
Bu sayfadaki her iddianın kaynak dosyası. Politika motoru açık kaynak
olduğu için, okuduğunuz her şey doğrudan doğrulanabilir.
Tespit sınıflandırması, güven kapısı, GID ile öldürme, karantina, geri yükleme
libsysmon/inc/edrdrvapi.hpp42–307
Sürücü → servis telemetri şeması, SysmonEvent kodları
libcore/inc/events.hpp19–117
Event numaraları, MLE_MASK, createMle
edrdata/compile_ptm.bat
Politikanın ptm.act olarak derlenmesi
edrdata/compiler.cfg10–138, 330–477
Olay sınıfı şemaları, varsayılan değerler, eski RF*/RP* takma adları
Kural Kimliği
RANSOM_BEHAVIOR / MLE_RANSOM_BEHAVIOR
baseType
1000008
Motor
PTM v2 (PatternsMatching / EVM)
Tespit Katmanı
Ring-0 (edrdrv.sys minifilter) + User-mode politika motoru
Kural Maddesi
16 (3 tanesi tekrarlı / ölü kod — Bkz. Bölüm 16)
Eşik
3 ayrı hedef / 60000 ms kayan pencere
Güven Seviyesi
3 — CRITICAL
Yanıt
Karantina (saldırgan ikilisi) + GID ile öldürme + ön-image geri yükleme
Lisans
Açık kaynak (OpenEDR fork: BSD-3 / VirusKat katkıları)
RANSOM_BEHAVIOR
MLE_RANSOM_CANDIDATE
MLE_RANSOM_EVALUATE
distinctCounter
saveContext
createEvent
imatch
StringMatcher
Shannon Entropy Q24
minifilter
IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION
isAsciiText
RansomShield
OwlyShield
Ring-0
PTM v2
EDR
anti-ransomware
✦ Destek, Katkı ve Bağış
Bu wiki ve MLE_RANSOM_BEHAVIOR dokümantasyonu, projenin açık kaynak
felsefesinin bir parçasıdır: hiçbir şey gizli değildir. Bu kuralı
okudunuz, kaynak kodunu incelediniz, hatta bu sayfadaki
bilinen kusurları bile gördünüz.
Projeye katkı yapmak, ek kural yazmak, ya da sadece finansal destek
(sunucu / test ortamı / imza sertifikasyonu masrafları) göstermek isteyenler için:
[email protected]. Tüm talepler doğrudan proje liderine ulaşır —
aracı yoktur, komisyon yoktur; kutunun içinde ne olduğunu bu sayfalarda
gördüğünüz gibi, bağış akışında da görürsünüz.
VIRUSKOV / WIKI / TESPİT KURALLARI / MLE_RANSOM_BEHAVIOR
MLE_RANSOM_BEHAVIOR
Fidye Yazılımı Tespit Kuralının Tam Anatomisi
OpenEDR × Hydra Dragon çekirdeği • PTM / PatternsMatching v2 politika motoru
Kural Kimliği: RANSOM_BEHAVIOR | Yayın Olay Tipi: MLE_RANSOM_BEHAVIOR | baseType: 1000008
Politika Kaynağı: OpenEDR/edrav2/iprj/edrdata/ptm.local.src • Satır 4636 – 5129
MLE_RANSOM_BEHAVIOR, bir dosyanın hash'ine, derleyicisine ya da paketleyicisine bakmaz.
Çekirdekte gördüğü tek şey davranıştır: bir süreç, kullanıcının kişisel
dosyalarına 60 saniye içinde en az 3 ayrı hedefe yazıyor, yeniden adlandırıyor
ya da siliyor; bu hedefler metin dosyası değil ve şifrelenmiş gibi
yüksek entropili veri taşıyor. Bu üçlü — kitle × hacim × içerik
— eşleştiği anda motor, saldırganın proses imajını karantinaya alır ve
süreci anında öldürür. Bu sayfa, bunun her tek satırını, her tek süzgecini ve
her bilinen sınırını açar.
01 Künye
The table below collects the rule's identifying details and technical parameters in one place.
All of these values are taken verbatim from the source code in the repository; none of them
is an estimate. Source lines and file paths are given in each cell.
New or unsigned ransomware is detected from the moment it is downloaded
and executed, without looking at any compiled-code signature. This is because the rule looks at
what the code does:
The kernel file system filter (minifilter) records a process that performs write / rename / delete operations on 3 or more distinct user files within 60 seconds.
The policy engine verifies that those files are not text (via an ASCII scan of the first 8 KiB) and have high entropy, like encrypted data.
It verifies that the targets are in a user profile (C:\Users\...) and outside AppData / Temp / Windows / Program Files.
If all conditions hold, an MLE_RANSOM_BEHAVIOR event is generated; the engine quarantines the process image file and kills the process by Global ID.
The kernel's RansomShield module restores the original file copies it saved earlier.
▶ What this means in practice
Whether the ransomware family was compiled yesterday or today, is packed,
or carries a name never seen before makes no difference. The rule describes a behaviour,
not a family. That is why zero-day ransomware goes through the same gate without
any signature database update.
03 Neden Bu Kural Bir İmza Değil, Bir Davranış Kuralı?
Classic antivirus thinking goes like this: “I have seen this file before, its SHA-256
is on the list, it is malicious.” This approach breaks in two places.
Approach
What it does
Breaking point
Hash signature
Looks up the file's digest in a fixed list
Change a single byte and the hash changes. Bypass: 1 second.
A new compiler version changes the signature. Bypass: one update.
AI-assisted static ML
Scores deviation from PE headers
Well-written malware mimics a legitimate PE header. Weak separation.
MLE_RANSOM_BEHAVIOR
Watches the file system I/O flow and recognises the encryption operation itself
The code may change, but the behaviour does not. Encryption is a mandatory step.
▶ Core idea
Ransomware has one unchangeable requirement: to read the user's file
and corrupt it irreversibly.
Code signature, packer, file name, error message, extension — all of these can change.
But the action of “writing to dozens of user files in a row and making them unrecoverable”
does not. The rule signs exactly this action.
04 Uçtan Uca Mimari: Olayın Yolculuğu
There are eight separate layers behind the rule. Let's follow the event's journey from start to finish.
[1] RING-0 — edrdrv.sys (minifilter)
IRP_MJ_CREATE / CLEANUP / WRITE / READ / SET_INFORMATION
IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION (mmap capture)
|
| LLE_FILE_CREATE / LLE_FILE_DATA_READ_FULL / LLE_FILE_DATA_WRITE_FULL
| LLE_FILE_MAP_READ / LLE_FILE_MAP_WRITE / LLE_FILE_DATA_CHANGE
| LLE_FILE_RENAME / LLE_FILE_DELETE
v
[2] LBVS WIRE PROTOCOL (libsysmon/inc/edrdrvapi.hpp)
Schema-based, parsed named byte stream
|
v
[3] libsysmon CONTROLLER (controller.cpp)
SysmonEvent -> Event enum mapping, "type" tag
|
v
[4] EventEnricher (eventenricher.cpp)
Process enrichment, processes[] chain, file object
-> file fields are a LAZY PROXY (64KB read only on demand)
|
v
[5] QUEUE: match_patterns (qsc/match_patterns.qsc)
|
v
[6] PTM POLICY ENGINE (ptm.local.src / RANSOM_BEHAVIOR)
|
| destination "IN" -> back to match_patterns (state machine)
| destination "OUT" -> enrich_mle queue (detection output)
v
[7] enrich_mle -> check_for_valkyrie -> transform_for_cloud -> output
|
v
[8] DetectionNotifier (detectionnotifier.cpp)
Trust check -> KILL by GID -> QUARANTINE -> RESTORE -> GUI
The key distinction is this: destination: "IN" keeps the event inside the engine
(this is not a detection but a state machine step); destination: "OUT", on the other hand,
sends the event out of the engine into the enrichment and response chain.
MLE_RANSOM_BEHAVIOR is emitted with OUT — that is, it is a real
detection.
▶ Why two queues?
IN events are fed back into the same queue; this lets the policy file
build a multi-stage state machine by generating events and sending them back to its own rule.
This is the cleanest way to build a “read → write → count → decide”
chain on top of a single file operation, and it is exactly the architecture of MLE_RANSOM_BEHAVIOR.
05 Altyapı: PTM (PatternsMatching) Motoru
The rule is defined in a JSON-based policy language. When compiled it is embedded in the product and
remains auditable — that is, every line on this page is a line of the real code
running in the product. The engine is written in C++
(libedr/src/policy_v2.cpp, policycompiler.cpp,
policyoperation.cpp, signcontext.cpp).
5.1 — Directives
Each rule clause consists of directives inside a "rule" dictionary:
Directive
What it does
Where in This Rule
condition
Filter. If absent, the rule always matches.
Absent in stages 1a/1b and 5; present in the candidate generation and decision rules
saveContext
Writes a record into a basket (basket) with key + data + lifetime (ms).
Stages 1a, 1b — RANSOM_FILE_READ_WRITE basket
loadContext
Reads from the basket. Using @context.* requires it.
Not used in this rule (see Section 16)
deleteContext
Deletes a key or group from the basket.
Stage 5 — when the process terminates
createEvent
Generates a new event. destination: IN / OUT / IN_OUT. With clone:false, only the fields in data are carried over.
Stages 2, 3 and 4 — the heart of the rule
distinctCounter
Counts distinct values in a sliding window and writes a boolean to the flag field when the threshold is exceeded.
Stage 4 — ransomFileDistinct basket
discard
Drops the event.
Not used
▶ Key engine constraint: one condition per rule
A rule dictionary can have only one condition key.
The engine stores directives in an unordered_map<Directive, DirectivePtr>;
if a second condition is written, the first is silently dropped. That is why
all nine filters in stage 4 are combined into a single "and" chain.
This is a deliberate design decision arising from a constraint of how rules are written, and
it is also documented explicitly in ptm.local.src.
5.2 — Operations and Evaluation Semantics
Operation
Arity
Meaning
In This Rule
imatch
1 value + pattern
Case-insensitive wildcard match
*\users\* etc.
!imatch
1 value + pattern
true if it does not match
Whitelist checks
and / or
≥ 2
Logical combination (no short-circuit, all are evaluated)
Filter chain
equal / !equal
exactly 2
Type-based equality
type == "OTHER", isAsciiText
greater
exactly 2
Integer comparison; null/bool/string → treated as 0
Entropy threshold
5.3 — Wildcard Matching Semantics
The imatch operator dispatches wildcard patterns to two separate engines, and
this choice directly affects performance:
Pattern Form
Compiled Engine
Meaning
*\Windows\*
StringMatcher, mode all
“contains \Windows\” — fast path, no regex
*.docx
StringMatcher, mode end
“ends with .docx” — the 1479 extensions take this path
C:\Windows\explorer.exe
StringMatcher, mode begin
“starts with” (so .exe.bak also matches)
*\*.*.*
POSIX regex (basic)
A wildcard in the middle falls through to the regex path; *→.*, ?→.
▶ Important distinction
Leading and trailing * characters are not “regex wildcards”;
they act as anchors and are converted to the begin / end /
all modes. That is why the 1479-extension list runs without compiling a regex,
as a single StringMatcher table — on a live system this means a cost of
tens of microseconds.
06 Kuralın Anatomisi: 16 Madde, 5 Aşama
The RANSOM_BEHAVIOR pattern consists of 16 rule clauses. These clauses are grouped into five logical
stages. Each stage is explained below together with the relevant source code.
STAGE 1Recording the Read Side — saveContext
Two rule clauses record that a non-system file has been read in full.
The goal is to keep the fact that “this process read this file” for 120
seconds. This is the basis for later building a “reader → writer” match
(see Section 16, item 3: this basket is currently only written, never read).
Clause 1a — LLE_FILE_DATA_READ_FULL
Captures the kernel event generated when a file is read from start to end
completely and sequentially. The minifilter emits it only when the condition
readInfo.nNextPos == nSizeAtCreation holds — so partial reads do not count.
Clause 1b — LLE_FILE_MAP_READ (read via memory mapping)
The same work, when done through mapping (mmap). Many modern ransomware strains
map the file into memory not with CreateFile + ReadFile but with
NtCreateSection + NtMapViewOfSection. This path
never sends IRP_MJ_READ, so clause 1a above
cannot catch it. That is why the minifilter turns the PAGE_READONLY page protection of the
IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION IRP
into a separate event.
▶ Why does this separate clause matter?
Without the LLE_FILE_MAP_READ and LLE_FILE_MAP_WRITE clauses,
mapping-based encryptors — most modern ransomware written in
Rust/Go/C++ — would be completely invisible. These two lines are
the key technical decision that lifts the rule to the level of “catches everyone who reads”.
STAGE 2Generating the Candidate Event — MLE_RANSOM_CANDIDATE
This ten-clause block is the heart of the engine. Any write,
modification, rename or delete on a non-system file
is turned into an MLE_RANSOM_CANDIDATE event. No decision is
made here; the event is only recorded.
Because of destination: "IN", this event stays inside the engine and is fed back into
the match_patterns queue to be processed again in stage 3.
Clause
Source Event
When It Is Generated (Kernel)
Note
2a
LLE_FILE_CREATE
After IRP_MJ_CREATE — file created or truncated
First step of the pattern that creates a new encrypted file
2b
LLE_FILE_DATA_WRITE_FULL
After IRP_MJ_CLEANUP — file written from start to end
If the default clone: true were used, all fields of the triggering event
would be copied automatically into the new event. With clone: false, the engine carries only
the fields in the data array that are listed by hand. This makes it visible, at
every step of the chain, which field is deliberately carried (and which is deliberately
not carried) — so the policy gains readability and
auditability.
STAGE 3From Candidate to Evaluation — MLE_RANSOM_EVALUATE
The engine takes the MLE_RANSOM_CANDIDATE event and turns it into an
MLE_RANSOM_EVALUATE event. This single-clause rule is designed as
a pure step separated from the decision itself, and
does three things:
Relabelling: the event is no longer “a file was touched” but “suspicious file”.
Field normalisation: the destination field, which was @event.file in stage 2, is read here as @event.destination — that is, the field carried by the previous event.
Timestamp carry-over: time and tickTime are preserved, so the counter and the analysis timeline stay correct.
STAGE 4Decision — distinctCounter + Nine Filters → MLE_RANSOM_BEHAVIOR
The heart of the rule. Two things happen here at the same time:
distinctCounter runs: per process ID (PID), it counts how many distinct target files were touched in the last 60 seconds. When it reaches 3, @event.RansomThresholdMet = true is written.
condition runs: all nine filters must hold. If they do, the final detection event is emitted with OUT.
▶ Evaluation order: counter FIRST, condition AFTER
The compiler emits directives in a fixed order:
distinctCounter → condition.
So everyMLE_RANSOM_EVALUATE feeds the counter — the condition is
evaluated afterwards. The practical consequence is covered in detail in Section 16;
for now, know this: the counter counts “potential candidates”, not confirmed suspects.
ptm.local.src — STAGE 4, createEvent (5100-5116)createEvent / OUT — REAL DETECTION
In the candidate stage, quarantineTarget pointed to the file being encrypted.
In the final detection it is set to @event.process.imageFile.rawPath — that is,
the path of the attacker's own executable.
This is the most important architectural decision of the whole rule: it is not the victim file but the
offending process. Quarantine is applied to the ransomware binary; the user's
data is restored by RansomShield.
STAGE 5Context Cleanup — deleteContext
When the process terminates (LLE_PROCESS_DELETE), the engine tries to clear the read basket.
This is a hygiene clause that prevents memory bloat on a long-running machine.
Stage 1a/1b records are written with the group field omitted, while stage 5
tries to delete by group. Because the engine cannot make this match, cleanup
in practice happens via the 120-second TTL. This is not a functional
problem; it does not affect the rule, only the record lifetime is decisive.
Details in Section 16.
07 Dokuz Süzgeç, Tek Tek
The condition field of stage 4 is nine independent filters combined
in a single "and" chain. For the rule to fire, all of them
must hold at the same time. This is a closed-by-default (fail-safe)
design: it is built around missed detections, not false positives.
Actor whitelist. The “encrypting process” is checked,
not the “encrypted file”. Legitimate tools running from C:\Windows\ or
C:\Program Files\ (updaters, setup
wizards, antivirus engines) are filtered out here.
Why check the actor? A setup wizard
may also write under C:\Users\alice\Desktop; but a
setup.exe in C:\Windows\Temp\ does not write to dozens of user
files. Excluding the actor is a much more reliable
false-positive cutter than looking at the target.
Target whitelist. The target path itself must not resemble
any of the 8 system patterns. See Section 11. Without this filter, update engines,
browser caches and installer caches would
trigger the rule periodically.
Location narrowing. This is the only required positive condition: the target
must be inside a user profile. It is clear why ransomware
encrypts — personal data stores such as C:\Users\alice\Documents,
Desktop, Downloads, Photos. A tool that encrypts
C:\Windows or network drives is not targeted by the rule. This single condition
dramatically lowers the false-positive rate on corporate desktops.
Note: This pattern is compiled in StringMatcher “contains”
mode. The installation name always stays in English (\Users\), so it
also works on localised Windows installations.
Application data protection. Inside AppData, hundreds of applications such as Chrome, Firefox,
VS Code, Steam and Discord write to dozens of files continuously. These are “legitimate processes that write to many files”, and
the user's actually valuable data is not here. This filter shuts off the noisiest
source of false positives.
Conflict note: S3 looks for \AppData\ inside \users\; S2 already contains the *\AppData\* pattern. Therefore S4 performs exactly the same check as S2 and, as of today, is not an additional
filter — it is deliberately
repeated for defence in depth and readability.
Mark-of-the-Web protection. In NTFS alternate data streams,
Zone.Identifier is the “this file was downloaded from the internet” marker.
Mark of the Web attacks use exactly this stream:
by touching the MOTW stream of a downloaded file they make corporate files “internet-originated”
and trick the user with a warning. This filter pushes such techniques outside the rule's
scope.
S6equal(@event.destination.type, "OTHER")
The target is not an executable. The file type is determined by whether the extension is in the configured
executable list (.exe;.dll;.sys;.cpl;.mui;.tlb;.scr). The rule accepts only "OTHER" targets,
i.e. documents, spreadsheets, images, videos, archives, source code and databases.
Why does it matter? Malware modifying its own binary
(to gain persistence) does not trigger this rule — such actions belong to neighbouring rules such as
MLE_PUA_REGISTRY_WRITE and MLE_FILE_COPY. The rule has a single purpose: destroying user data.
A single purpose means few false positives.
S7has extension AND user-file extension AND entropy > 6
An AND&OR structure of three sub-conditions. 7a: the target (or source) must contain a
dot — extensionless temporary files are filtered out. 7b: the extension
must be one of the 1479-pattern user-data list. 7c:
Shannon entropy must exceed the threshold. Together these three answer the rule's question “is this a data file, and is it
encrypted?”
Practical result: a text editor writing plain text to C:\Users\alice\Notes.txt
is filtered out (7b: .txt is on the list, but filter 8
isAsciiText=true → filtered out). Ransomware encrypting the same file
passes.
S8!equal(@event.destination.isAsciiText, true) — THE REAL TEXT GUARD
The single strongest filter. The current first
64 KiB of the file on disk is read, but whether it is text is decided by looking at only the first 8192 bytes (8 KiB):
if the number of bytes outside \t \n \r and 0x20-0x7E reaches the threshold
8 KiB × %15 + 1 = 1229, the file is marked
“not text” (fail-fast).
What does it read, and when? The field reads the current on-disk content at the moment
the rule is evaluated — i.e. the
ciphertext the attacker has just written. It does not look at the extension, the file name or a “suspicious file”
label. Because stage 4 mostly runs on LLE_FILE_DATA_WRITE_FULL,
the file is already encrypted at this point and the scan sees the
ciphertext. The same filter, on the other hand, filters out plain text written to the same file by a text editor.
Measurement window: 8 KiB, threshold 15% + 1, fail-fast. The implementation reads up to 64 KiB
in a single call, but analysis is done only on the first 8192 bytes
and stops as soon as the threshold is reached — no further bytes are examined:
libsyswin/src/filedataprovider.cpp (1195-1210) — verbatimFIRST 8 KiB ONLY
// Content-based ASCII-text check. Exact C++ port of:// fn looks_like_ascii_text(data: &[u8]) -> bool// Empty input -> false. Samples at most the first 8192 bytes, fail-fast once// the non-printable count reaches 15% + 1. Printable set: \t \n \r 0x20..=0x7E.bool FileDataProvider::is_look_like_ascii_text(const uint8_t* data, size_t len)
{
if (data == nullptr || len == 0) returnfalse;
const size_t sampleLen = len < 8192 ? len : 8192; // <-- ANALYSIS WINDOW 8 KiBconst size_t threshold = sampleLen * 15 / 100 + 1; // 8192'de = 1229 bayt
size_t nonPrintable = 0;
for (size_t i = 0; i < sampleLen; ++i)
{
const uint8_t b = data[i];
const bool printable = (b == '\t' || b == '\n' || b == '\r' || (b >= 0x20 && b <= 0x7E));
if (!printable && ++nonPrintable >= threshold)
returnfalse; // <-- FAIL-FAST
}
returntrue;
}
Flow (1217-1236).isAsciiTextFile() → if the file object has the deleted flag:
no disk read at all, false → the file is opened via getFileStream()
in Read | ShareRead | ShareWrite mode (if needed, a second open is attempted with
impersonation using the process token) → up to 64 KiB is read
from offset 0 in a single call → if 0 bytes were read, false →
the 8 KiB analysis above.
Thanks to the ShareWrite flag, the file can be read even while the attacker
holds open write handles at that moment — i.e. the file is not locked
while encryption is in progress, and the scan still sees the content.
Why is 8 KiB enough? The first 8 KiB of a plain-text file consists of
printable characters anyway; in ciphertext, reaching 1229 bytes (~15%) is
practically guaranteed given a random distribution. This cuts the cost from a 64 KiB
scan to 8 KiB + fail-fast.
Why is it so decisive? A JPEG, an MP4, a .docx
(ZIP), an .xlsx, a database file — they may all look like
text, but none of them is text. The rule filters out only files that are clearly text.
This draws the line between “a compressed folder copy that only looks
like encryption” and “real encryption”.
Fail-closed behaviour: if the file was deleted, cannot be opened or 0 bytes were read,
the result is false— i.e. “not text”. If the rule cannot gather
evidence, it errs on the strict side and does not filter the file out. This is a deliberate choice:
the cost of a false negative (missed real encryption) is higher than the cost of a false positive (a user's
legitimate file being quarantined).
Trigger-specific detail: on LLE_FILE_CREATE
the file is still empty, so 0 bytes are read and the filter passes;
on LLE_FILE_RENAME / LLE_FILE_DELETE the content is
whatever it is at that moment — for a not-yet-encrypted file the filter may filter it out
(on LLE_FILE_DELETE the deleted flag additionally returns
false without reading). Since the rule's main trigger is LLE_FILE_DATA_WRITE_FULL,
this is not a problem in the normal encryption flow.
The lifeblood of the rule. Each of the other eight filters on its own describes a single file
operation; this filter turns it into an attack. Within 60 seconds,
3 or more SEPARATE target files must have been touched.
See Section 8. An operation on a single file, however suspicious,
never triggers. This is what separates “a user edited a photo”
from “someone is encrypting the disk”.
08distinctCounter: Kayan Pencerede Ayrı Değer Sayacı
distinctCounter is the engine's most distinctive directive. It is not a simple counter:
repeats of the same value do not add to the count. This small but critical distinction
explains why the rule says “3 files”.
"distinctCounter": {
"basket": "ransomFileDistinct", // separate counter space (container)
"key": "@event.process.pid", // EACH PROCESS IS COUNTED SEPARATELY
"values": [ "@event.destination.path" ], // counted value: TARGET PATH
"threshold": 3, // 3 AYRI hedef
"window": 60000, // 60 saniye kayan pencere
"flag": "@event.RansomThresholdMet"// result is written here
}
8.1 — Semantics: what is counted and what is not?
Property
Behaviour
Relevance to Ransomware
Counted
Non-empty values within the same key that are DISTINCT from each other
“How many different files did it touch?” — the right question
Not counted
Repeated writes to the same file
A backup tool writing the same file 500 times → counter = 1, threshold not crossed ✓
Key
@event.process.pid — per PID
Process isolation: two different malware processes cannot fill each other's counter; a backup tool cannot pollute the counter
Window
60000 ms; expired entries are pruned sporadically on calls
Slow, scattered encryption (1 file every 40 seconds) does not cross the threshold — a deliberate trade-off
Flag
While Size >= threshold holds, it is true — level-triggered (latching), not edge-triggered
Until the window expires, detection continues; every new file triggers again
Cleanup
There is no explicit reset path; entries only expire with the window
See Section 16 — an intended or unintended consequence
8.2 — Concrete Scenario
PID 4711 — suspect.exe (second 0)
write C:\Users\alice\Documents\sozlesme.docx -> DISTINCT #1 counter = 1
write C:\Users\alice\Photos\tatil.jpg -> DISTINCT #2 counter = 2
write C:\Users\alice\Desktop\butce.xlsx -> DISTINCT #3 RansomThresholdMet = TRUE
|
+--> S1..S8 verified ==> MLE_RANSOM_BEHAVIOR
|
+--> write C:\Users\alice\Videos\dogum.mov
-> DISTINCT #4 flag STILL TRUE
==> second MLE_RANSOM_BEHAVIORAfter 60 seconds: all entries go stale, the window is cleared
Second 61: a new write -> the counter starts again from 1
▶ Why 3, and why 60 seconds?
The threshold of 3 balances three things at once: (a) it filters out legitimate software
that does not touch more than one file, (b) it screens out two-file sample ransomware specimens
(common among test samples), (c) it does not delay detection, because in real encryption runs the
file count quickly climbs into the tens.
The 60-second window captures the reasonable time gap between “frequent user activity” and “bulk
operations at machine speed”. A human does not encrypt
3 documents in 60 seconds; ransomware does hundreds within seconds.
A practical trade-off: sophisticated “slow-crawling” encryptors that wait several minutes
per file can evade this window. What catches them will not be another
rule but combined temporal behaviour. This is a known limit of the rule,
honestly documented.
09 Dosya Nesnesi: Hangi Alana Bakılıyor?
The rule's @event.destination.* and @event.file.* expressions
do not refer to a raw path from the driver but to fields of an enriched file
object. This object is built lazily: nothing is read from disk
unless a field is actually read.
Field
Source
Use in the Rule
path
Normalized, human-readable path
S1, S2, S3, S4, S5, S7, S9 — the path itself
type
Is the extension in the configured executable list? (EXECUTABLE / OTHER)
S6 — == "OTHER"
isAsciiText
64 KiB is read from disk (offset 0, ShareRead|ShareWrite, with impersonation if needed); text analysis is done only on the first 8 KiB (8192 bytes), threshold %15+1 (1229 bytes in 8 KiB), fail-fast. Read time: the current content at the moment the rule is evaluated — i.e. ciphertext if the file has been encrypted.
S8 — != true
size
Lazy file size
Not used directly (the kernel already applies the full-read / full-write threshold)
rawHash
xxh64 over the read/write sequence
Not used directly in this rule; used in the MLE_FILE_COPY rule
▶ Security and performance value of lazy evaluation
isAsciiText means a disk read on every evaluation. The system builds it as a
lazy proxy object (lambda proxy): no byte is read unless the rule
actually touches that field. If none of the 10 kernel triggers
read this field, not a single byte is spent.
When it is touched, the work is cheap on two levels: the 64 KiB I/O
is done in one call, but analysis completes fail-fast within the first 8 KiB.
So for typical ciphertext the decision is made after roughly 1229 bytes have been read
— the remaining 63 KiB are never scanned. This is what makes an EDR
usable on a live system.
10 Shannon Entropisi: Q24 Sabit Nokta ile Hesaplama
Entropy is a measure of how “random” data is. Encrypted data
(AES, ChaCha) is close to the theoretical maximum of 8 bits/byte.
Plain text sits in the 3.5–5 band. This difference turns the question “is it encrypted?”
into a numerical criterion.
10.1 — Algorithm
edrdrv/src/ShanonEntropy.cpp + ShanonEntropy.hRING-0 — NO FLOAT
// ENTROPY_ONE_Q24 = 2^24. Range: [0, 8 * ENTROPY_ONE_Q24]
// To convert to float: (double)result / (double)ENTROPY_ONE_Q24
constexpr ULONGLONG ENTROPY_ONE_Q24 = 1ULL << 24;
ULONGLONG shannonEntropyQ24(PUCHAR buffer, size_t size)
{
if (!buffer || size == 0) return 0;
// 256-bucket byte histogram
ULONGLONG bkt[256] = {};
for (size_t i = 0; i < size; i++) bkt[buffer[i]]++;
ULONGLONG slQ24 = OwlyLog2Q24((ULONGLONG)size); // log2(N)
ULONGLONG wsum = 0;
for (ULONG i = 0; i < 256; i++)
if (bkt[i]) wsum += bkt[i] * OwlyLog2Q24(bkt[i]);
ULONGLONG avg = wsum / (ULONGLONG)size; // (1/N) * SUM n_i*log2(n_i)
ULONGLONG eQ24 = (slQ24 > avg) ? (slQ24 - avg) : 0; // H = log2(N) - (1/N)*SUM(...)if (eQ24 > c_EntMaxQ24) eQ24 = c_EntMaxQ24; // clamp to 8return eQ24;
}
10.2 — Why Q24 fixed point?
▶ Floating point is forbidden in the kernel
This computation runs in Ring-0, in the post-operation of every write.
Floating-point operations (SSE/MMX) are strictly forbidden in the Windows kernel
— an exception leads to an aborted operation and system instability.
That is why the log2 function is rewritten in integer
arithmetic using an atanh series (terms /3, /5, /7, /9, /11, /13),
with 24 fractional bits (Q24), and the result is hard-clamped to
8 × 2^24. Precision is about
6×10^-8 bits/byte — more than enough for the decision threshold of “6”.
10.3 — Where and how is it sampled?
Property
Value
Sampling point
Only write operations (pre/post write, every MDL page and every direct buffer)
Aggregation
The maximum chunk entropy in the sequence
Publication
Only on the LLE_FILE_DATA_WRITE_FULL event (owlyEntropy + owlyIsEntropyCalc=1)
Type
uint64 Q24 fixed point — raw value, unscaled
Accuracy note
Not IEEE-exact because the series is cut off after 6 terms and the mantissa is truncated; deviation of a few ULPs in practical ranges
10.4 — Typical values
Content
Theoretical H (bits/byte)
Q24 Raw Value
Result in the Rule
Fully constant (zeros)
0,0
0
FILTERED
English text, source code, .txt/.js/.html/.json
3.5 – 5.0
58,720,256 – 83,886,080
Filtered by S8
Compiled PE (mix of code + data)
6.0 – 7.2
100,663,296 – 120,795,955
PASSES
Compressed archive (.zip/.7z/.zst)
7.9 – 8.0
≈132,120,000 – 134,217,728
PASSES
Fresh AES/ChaCha ciphertext
7.9 – 8.0
≈132,120,000 – 134,217,728
PASSES
▶ Scaling defect (known limitation)
The driver writes entropy as a raw Q24 integer (0 … 134,217,728),
but the policy rule compares it with 6 — i.e. it compares against the
raw Q24 value. This means “pass if greater than 3.6×10^-7
bits/byte”; in practice it turns into the question “was this a full-write
event?”.
The scaling is mentioned in a filemon.cpp comment (“the Rust side divides by 1<<24”),
and it applies to the OwlyShield Rust consumer;
no scaling is done on the PTM/C++ path. As a result, the entropy distinction that
actually works is filter 8 (isAsciiText), while block 7c acts as a “was it a full-write
event” filter. Details and the proposed fix:
Section 16.
11 Beyaz Liste ve Uzantı Gözcüsü
11.1 — System Exclusions (8 entries)
This list feeds the rule's two hardest-working filters (S1 and S2). The comment line in the source
states it explicitly: “System and browser cache paths excluded from ransomware
tripping (FP reduction)” — system and browser cache paths are excluded
to reduce false positives.
// System and browser cache paths excluded from ransomware tripping (FP reduction)"ransomSystemExclusions": [
"*\\Windows\\*", // mode "all" <-- contains \Windows\"C:\\Windows\\explorer.exe", // mode "begin" <-- STARTS WITH"*\\Program Files\\*", // mod "all""*\\Program Files (x86)\\*", // mod "all""*\\ProgramData\\*", // mod "all""*\\AppData\\*", // mode "all" (case-insensitive)"*\\Temp\\*", // mod "all""*:Zone.Identifier*"// mod "all" <-- NTFS ADS
],
▶ A subtle detail of the C:\Windows\explorer.exe line
Since this pattern contains no wildcard, it becomes a prefix (begin) match.
That is, any path that has C:\Windows\explorer.exe as a prefix matches
— such as C:\Windows\explorer.exe.bak. This breadth is deliberate: it is also filtered out when the installer's
desktop backup tool creates explorer.exe.bak.
Likewise, *\Windows\* already covers everything under C:\Windows\; this
line is redundant, but because the name explorer.exe is given
explicitly, it appears to have been kept as a fallback that would still apply if the \Windows\ pattern
were narrowed later.
11.2 — User File Extension Watchlist (1479 entries)
The source comment says: “Extensions of user files whose encryption is treated
as ransomware-relevant. Scopes the file-encryption candidate so extensionless / temp files
do not FP.”
High-value “original work” files, the type most often lost
Media / creative
*.blend *.psd *.indd *.cdr *.aep *.prproj *.raw
Photo and video archives — usually the directory with the most photos
Structurally broad entries
*.0 *.1 ... *.100, *.log *.tmp *.dat
Low signal, but neutralized when combined with filters 7a & 7b
▶ Design that narrows the list: two-layer protection
ransomFileExtensions deliberately relies on a broad list
— 1479 extensions cost almost nothing in a single StringMatcher table.
Narrowing is left to the subsequent filters (7a: is there a dot; S8: is it text
or not; S9: 3+ distinct targets). This is the right architecture for preserving detection power
while removing the burden of constantly keeping a list
up to date: a broad candidate pool + layered strict filters beats a narrow candidate pool that constantly needs updating.
12 Kernel Tarafı: Hangi IRP, Hangi Olay?
All of the rule's triggers are generated in Ring-0. The minifilter registers seven IRP major
functions. This registration determines the entire rule:
“Open the file, then write” is distinguished from “create and write”
Backup name exception
Files whose path contains HydraDragonBackups are skipped
The product's own restore mechanism does not trigger itself — an important safeguard
▶ Important: this technique is resilient to BYOVD and anti-EDR evasion
Ransomware either writes in place over the original file, produces a duplicate
file, or creates a new file and deletes the original. All three models leave an
“IRP_MJ_CREATE + WRITE/CLEANUP + SET_INFORMATION” trace —
captured without API hooking. Classic hook-based AVs
(usermode) do not see this; a Ring-0 minifilter does.
12.3 — The mmap path: catching the invisible
edrdrv/src/filemon.cpp (2564-2573) — driver comment, verbatimWHY A SEPARATE IRP?
/* Reads/writes via mmap produce NO IRP_MJ_READ / IRP_MJ_WRITE.
NtCreateSection + NtMapViewOfSection map directly into
memory. So the only visible signal is the section acquisition IRP. */
The driver classifies PAGE_READONLY protection as read intent, and PAGE_READWRITE /
PAGE_WRITECOPY / PAGE_EXECUTE_READWRITE / PAGE_EXECUTE_WRITECOPY
protections as write intent. The page protection is written to the accessMask
field.
▶ The choice of modern encryptors
A significant share of professional ransomware written in Rust (an open-source language), Go and C++
processes files with mmap — because this path is faster
and leaves no trace in user-mode API hooks. Without the LLE_FILE_MAP_READ and
LLE_FILE_MAP_WRITE items, this class of ransomware would be completely
invisible to the rule.
13 Neden İki Ara Olay Var?
The intermediate events MLE_RANSOM_CANDIDATE and MLE_RANSOM_EVALUATE
may look like “unnecessary processing”. They are not. They have three concrete benefits:
Benefit
Description
Decoupling
Stage 2 only aggregates, stage 4 only decides. If the two responsibilities were combined in one place, the threshold calculation would be buried inside the generation logic and become impossible to change.
Rule traceability
Every step appears under its own event name in the log and debugger flow. “A candidate was produced but the threshold was not met” can be distinguished from “No candidate was produced at all” — a critical diagnostic capability in production.
Policy extensibility
The MLE_RANSOM_EVALUATE output is a public hook point. A future rule (e.g. “different threshold, different window”) can attach to the same candidate stream without touching the existing rule at all.
▶ An enterprise architecture lesson
Separating aggregation from decision is a standard
pattern in large event processing systems. The rule applies this pattern to file system observation rather than a database.
It is cheap, auditable and extensible.
14 Yanıt Zinciri: Tespitten Sonrası Ne Olur?
Producing a detection is not the end. The MLE_RANSOM_BEHAVIOR event
passes through the OUT chain of enrichment, trust check, kill, quarantine and restore.
The flow below is the actual order inside DetectionNotifier::execute("put").
MLE_RANSOM_BEHAVIOR (baseType 1000008, OUT)
|
|--> [1] severity = 3 (CRITICAL) + alert_kind = "critical" <-- fixed for every detection
|
|--> [2] Title synthesis + quarantineTarget lock
| <- @event.process.imageFile.rawPath (ATTACKER'S BINARY)
|
|--> [3] OwlyShield verdict synchronization (owlyshield_update_process_verdict)
|
|--> [4] TRUST CHECK (detailed below)
| should_trust_company_whitelist = true
| should_trust_comodo_fls_cloud = true
| -> trusted signed / FLS SAFE ==> kill + quarantine CANCELLED
| -> malicious / PUA vendor ==> strict enforcement
|
|--> [5] KILL PROCESS — edrdrv IOCTL
| DeviceIoControl(\\.\\{157980D8-09B4-4580-B8B6-D32971D056DA})
| msgType = 6 (MESSAGE_KILL_ONLY_GID), target = Global ID
|
|--> [6] QUARANTINE — owlyshield_ransom.dll
| owlyshield_dll_quarantine_file(dosyaYolu)
| + recordMalwareDetection() (persistent database record)
| on failure, via: \\.\pipe\HydraHipEvent
| "Malicious file locked! Please restart your computer..."
|
|--> [7] RESTORE — EventEnricher::rollbackRansomBackups()
| Process, root process, GID only — pre-image restore for all three
| (original copies RansomShield saved under %PROGRAMDATA%\HydraDragonBackups
| before modification)
|
+--> [8] GUI notification + append to 1000-entry ring buffer
14.1 — Why is quarantineTarget the process image?
▶ A critical architectural decision
At the candidate stage, quarantineTarget points to the file being encrypted.
In the final detection, the process's own image file is assigned.
This is the rule's biggest distinguishing feature. Classic EDRs usually
quarantine the victim file — this does not protect the user's data,
it only removes the file flagged as malicious. Viruskov instead
destroys the tool and restores the data. The difference between the two is whether a product
is a “detector” or a “rescuer”.
14.2 — Trust flags: should_trust_*
Flag
Value
Effect
should_trust_company_whitelist
true
Allows the call to owlyshield_is_trusted_company_signer. A binary signed by a trusted organization → kill + quarantine are cancelled (the alert record is still kept).
should_trust_comodo_fls_cloud
true
Vetoes if the FLS cloud returns verdict = 1 (SAFE). It also vetoes on verdict = 4 (FAIL/ERROR) — so a cloud outage does not paralyze the detection system.
owlyshield_is_malicious_company_signer
overrides the trust flags
If a malicious / PUA vendor is detected, the verdict is forced to verdict = 2 and strict enforcement takes effect; the whitelist is disabled.
▶ This choice is deliberate and correct
The ransomware's “executed file” is often an unsigned intermediate binary
(LiveWire, PowerShell, script host, code with a stolen signature). That is why the
should_trust_* flags are kept enabled in this rule
(not false). This keeps the enterprise-security assumption “signed = safe”
on the right side. At the same time, since the
“trusted signed → encrypting exe” scenario is technically close to impossible,
this setting produces no FPs in practice, and only handles signed but compromised
(supply-chain) cases correctly.
▶ What neutralization does not do
On the put path, network blocking (firewall / IPC block),
bootkit / driver cleanup or sending the MLE to the cloudis not performed. These belong to separate modules. MLE_RANSOM_BEHAVIOR
focuses on a single job: stop the attacker, recover the data, notify the analyst.
15 Neden Mükemmel Bir Ransomware Kuralı?
“Excellent” here means: independent of zero-day status,
target-focused, evidence-based, and provably restorative.
Below are twelve concrete reasons.
R01 • INDEPENDENCE
Completely independent of the code
The rule does not look at the file's content, hash, compiler, packer or even its size. Even if the malicious code is completely changed, repacked or rewritten in a new language — the rule works the same. This alone provides zero-day coverage.
R02 • NECESSITY
Encryption is a mandatory step
For ransomware to achieve its goal, it must corrupt the file irreversibly. “Destroy", “encrypt", “remote wipe" — all three leave the same file system trace. This ties the rule to a mandatory behaviour: it cannot be skipped.
R03 • NAME-AGNOSTIC
Encryption is family-agnostic
Family, variant, build date, language, packer — none of them matter. The rule does not say “this is LockBit”, it says “this is encryption”. That is why new variants are not separate work.
R04 • RING-0
Observation in the kernel
Detection looks at IRPs, not usermode hooks. This provides two critical advantages: (a) usermode API evasion does not work, (b) wiping the antivirus from memory via BYOVD does not prevent detection — the engine is already running.
R05 • MMAP
Catches the invisible path
NtMapViewOfSection produces no IRP_MJ_READ/WRITE. Thanks to the LLE_FILE_MAP_READ / MAP_WRITE items, mapping-based encryptors are caught as well. Many EDRs miss this path entirely.
R06 • VOLUME
A single operation is not enough
The threshold of 3 distinct targets in 60 seconds completely rules out single-file operations. This is the rule's most important FP safety net: “a user worked on one of their files” and “someone is bulk-encrypting” are separated mathematically.
R07 • DISTINCT COUNTING
Repeats are not counted
distinctCounter counts distinct values. A backup tool writing to the same file 500 times cannot fill the counter. This makes the critical distinction between “many operations" and “many different files".
R08 • PROCESS ISOLATION
Key = PID
The counter is kept per process. Two different applications cannot fill each other's counter; a browser extension cannot “inherit" another process's progress. This breaks false-positive chains in enterprise environments.
R09 • CONTENT EVIDENCE
Content, not file name
isAsciiText measures the file's actual content: it looks for 15%+1 non-printable bytes in the first 8 KiB read from disk. It does not say “malicious because the name contained .doc", but “malicious because this is no longer plain text". Deciding by extension would constantly push enterprise email encryption and document processing software into false positives.
R10 • CONTROL MECHANISM
Lazy evaluation
Content scanning runs only when the rule actually touches that field. When it does, 64 KiB of I/O is performed, but the decision is made fail-fast on the first 8 KiB. The same rule is usable on a live system and has a provable FP rate. Not “tight but slow".
R11 • RECOVERY
Not just detection, but recovery
Quarantine is applied to the attacker's binary, and the user's data is restored (RansomShield pre-image). A system where the attack was detected but the data was lost does not count as recovered. This rule combines detection with action.
R12 • AUDITABILITY
Not a black box, but a readable policy
The rule sits in JSON, as 16 items, in full. Everything you are reading now exists verbatim in the product. The answer to “Why did it raise this alert?” comes from a screenshot, not a support ticket. This is the project's core philosophy: not Security by Obscurity, but Security by Legibility.
15.1 — How would an attacker try to bypass this rule?
To test the safe side, one has to answer a red-team question:
Attacker Technique
Effect on the Rule
Why
Packing / polymorphic code
INEFFECTIVE
The rule looks at file system operations, not code
Bypassing ReadFile/WriteFile via API hooking
INEFFECTIVE
The IRP layer is observed; the usermode API is not invisible, it is irrelevant
Direct NtWriteFile / ZwWriteFile
INEFFECTIVE
Ends up as the same IRP
Built-in file system APIs (deletion via SetFileInformationByHandle)
INEFFECTIVE
IRP_MJ_SET_INFORMATION is monitored
Pre-filtering with a file system filter (minifilter)
HARD — malicious
A running “legitimate" minifilter is visible; it is separate software that must itself be identified
Bulk encryption via WMI / remote management
INEFFECTIVE
Still the same file system operation
Encrypting only the C:\ root (outside user folders)
OUT OF SCOPE
Filter S3 requires \users\ — this is a deliberate limit (see Section 16)
Slow encryption, waiting several minutes per file
MISSED
Exceeds the 60-second window — known limit (see Section 16)
Writing under AppData first, then moving from there
OUT OF SCOPE
S2 & S4 filter out \AppData\ completely
Delete backups, then encrypt
INEFFECTIVE
The neighbouring rule MLE_RANSOM_SHADOW_COPY_DELETE takes over (Section 17)
Targeting plain text (encrypt only .txt)
EVADES
The S8 isAsciiText guard deliberately skips text files — an FP-reduction choice
▶ Mathematical summary
Covered: Signature changes, packing, API evasion, complete code
rewrites, mmap, built-in APIs, new language, new compiler, remote execution. Bypassed by: Slow encryption, disk-root encryption, plain-text-only targeting. This is a commercial trade-off. Every EDR makes this trade-off; Viruskov does
not hide it, but documents it exactly as on this page.
16 Dürüst Sınırlar ve Bilinen Kusurlar
This section was written deliberately. Describing a product's strengths is easy;
describing its weaknesses on the same page, with the same candour is what makes
trust human. The items below are
real findings identified through source code review — not assumptions, not malice, just plain fact.
16.1 — Limits by design
#
Limit
Impact
Fix
1
The 60-second window misses sophisticated encryptors that wait several minutes per file
Medium — any high-volume encryption is still caught
Increase the window to 5-10 minutes, raise the threshold to 5-10, or add a temporal rate (files/s) metric
2
The S3 *\users\* requirement leaves encryptors targeting the disk root or network drives out of scope
Low — these targets usually do not hold user data
A new rule: MLE_RANSOM_BEHAVIOR_SYSTEM (path-independent, excluding C:\Windows)
3
Entropy is measured only on full-write events — no entropy is produced for LLE_FILE_MAP_WRITE
Low — S8 isAsciiText already covers this path
Sampling mmap pages in preAcquireForSectionSync
4
S4 is the same check as S2 (double check)
None — defence in depth at zero cost
Recommended to keep; high value for readability and as an example
5
ransomFileExtensions is too broad (numeric and short extensions, *.0 … *.100)
Low — 7a + S8 + S9 filter it together
Narrow the list to real victim extensions. Note: the "*.2538)" entry on line 165 is a typo.
16.2 — Defects verified directly in the code
▶ DEFECT 1 — Three rule items are duplicated verbatim
The LLE_FILE_RENAME (2f), LLE_FILE_DELETE (2g) and
RF4* (2h) items — each is written twice.
Of the 16 items in total, 3 are redundant.
Impact:None. The same
MLE_RANSOM_CANDIDATE is produced for the same event; because the counter counts distinct values,
the second one does not change the counter. However, it hurts the policy's readability,
and if one copy is fixed later, the other will diverge.
Fix: Delete items 2f', 2g' and 2h'. 16 items → 13 items.
▶ DEFECT 2 — RF10* and RF4* are dropped at compile time
The policy compiler expands eventType keys containing wildcards
by prefix matching and looks only at event types that appear
without wildcards in the same policy. For RF10* the prefix is
"RF10", for RF4* it is "RF4" — both produce
zero matches, the key is removed and the rule silently
disappears.
Root cause: The RF* aliases (event class names of the v1 / legacy EVM
engine: RF4 = LLE_FILE_DELETE, RF10 = LLE_FILE_DATA_CHANGE)
are not defined in the v2 engine. The names that should be used are
LLE_FILE_DELETE and LLE_FILE_DATA_CHANGE.
Impact:No functional loss. The two intended
events (LLE_FILE_DATA_CHANGE = item 2d, LLE_FILE_DELETE =
item 2g) are already covered directly. All that remains is that three rules disappear from the compiled
output and the compiler emits no warning.
Fix: Delete items 2e, 2h and 2h' entirely; this does not change
the rule's logic.
▶ DEFECT 3 — The read bucket is written but never read
Stages 1a and 1b write records to the RANSOM_FILE_READ_WRITE
bucket via saveContext. However, there is no loadContext inside RANSOM_BEHAVIOR that reads
this bucket. Stage 2 triggers only on LLE_FILE_CREATE
and write/map/change/rename/delete events —
read events are not triggers.
Impact:Does not affect the rule (the threshold decision
is made entirely on the write side). It only wastes some memory for 120 seconds.
Stage 1 is a fully legitimate foundation ready for future work: a rule that wants to
build a “reader → writer” correlation
can attach here.
Fix options: (a) remove Stage 1, (b) add a
loadContext check for “was this file read first?”
(needed for the “physical copy + delete” model), (c) leave it as is.
▶ DEFECT 4 — Stage 5 cleanup does not match
Stage 1 records are written without a group field; stage 5 tries to delete them
with group: "@event.process.id". Because the engine stores the records
with an empty group marker, the group-based delete finds
nothing.
Impact:Not functional — the 120-second
TTL already does this job correctly. The only effect is that if the process ends earlier, the record
stays for 120 seconds unnecessarily.
Fix: Add "group": "@event.process.id"
to stages 1a/1b.
▶ DEFECT 5 — Entropy is not scaled
The driver writes entropy as a raw Q24 integer; the policy compares it with
6. So owlyEntropy > 6 in practice does not ask
“is the value greater than 6?” but rather “is the value greater than almost zero?”
In addition, of the four branches these never fire:
@event.destination.owlyEntropy (not in the schema;
the field is at the event root), @event.destination.entropy / @event.entropy
(not defined anywhere). Only the @event.owlyEntropy
branch works.
Impact:Medium. Block 7c in practice becomes a
“was this an LLE_FILE_DATA_WRITE_FULL event?” filter.
The real entropy discrimination is carried by the 8th filter (S8).
Fix: (a) in the enricher, convert to decimal with owlyEntropy / (1<<24)
and produce a file.entropy field, (b) write the threshold in Q24 units:
greater(@event.owlyEntropy, 100663296) (= 6 × 2^24),
(c) remove the non-existent destination.owlyEntropy / entropy branches.
▶ FLAW 6 — @event.source is always null
The source field is populated only in MLE_FILE_COPY* events.
Because MLE_RANSOM_CANDIDATE is produced from LLE file events,
source is never carried over — it is not read
at the candidate stage, and it stays null at the evaluation stage.
Impact:Very low. The second branch of 7b
(imatch(ransomFileExtensions, @event.source.path)) always returns
false; the same holds for the second branch of 7a. These two branches are
dead code and can be removed.
▶ FLAW 7 — Comments do not match the code
The stage 4 comment in ptm.local.src contains these statements:
“The entropy > 6.8 clause...” and
“plain explorer copies of text .js must never reach the
counter”.
Both statements are wrong: (1) the threshold in the code is 6,
not 6.8; (2) because, relative to the
condition, the counter runs first, text copies do feed the counter (the rule's own filter drops
them, but the counter still sees them).
Impact:None — it matters only because the
comment is misleading. Fix: Align the comment with the actual code.
16.3 — Build and production pipeline notes
Developer build tool:edrdata/compile_ptm.bat,
edrcon.exe compile -o ptm.act common.src ptm.local.src. The generated
ptm.act is synced to the srcBinInstall and srcBinUpgrade
directories.
The product also compiles it: The policy is recompiled when the service
starts; the developer script is only a convenience.
Boundary test: For distinctCounter only the threshold and
window are set; the threshold, window, key and counted value all
come from the policy file — the engine has no rule-specific constants.
▶ Why does this page exist?
The quality of an EDR is measured less by its error rate than by how quickly and honestly
it admits its errors. The seven flaws above may not be fixed yet;
but they are known. This is a concrete application of the project's “Security by
Legibility instead of Security by Obscurity” philosophy.
17 Komşu Kurallar: Tek Başına Yaşamaz
MLE_RANSOM_BEHAVIOR is one part of the ransomware defense. The neighboring
rules below are complementary layers that work alongside it.
1.vssadmin delete shadows → MLE_RANSOM_SHADOW_COPY_DELETE 2.CryptEncrypt calls → CRYPTO_API_MASS 3. User files are read → RansomShield saves a pre-image backup 4. Files are encrypted → MLE_RANSOM_BEHAVIOR (fires on the 3rd file) 5. The process is killed and the binary is quarantined 6.rollbackRansomBackupsrestores the data
18 Analist El Kitabı
How does a SOC analyst or security researcher get value from this rule?
The path below is the fastest way to read the right parts.
18.1 — The 8 fields to check first after a detection
Field
Question to Ask
Key Value
quarantineTarget
Which binary was quarantined?
File name, path, signature
process.id / process.pid
What is the global ID? (kill target)
Important — used in the analysis trail
process.cmdLine
How was it started?
powershell -enc, mshta, wmic clues
process.imageFile.rawPath
Where was it located?
%APPDATA%, %TEMP% = high priority
destination.path
Which file did it touch?
Last target hit (single file; if < 3, the threshold was not met)
destination.abstractPath
What is the target's “logical” name?
Resolves tokens such as %appdata%; more readable in the GUI
processes[]
Who started this process?
Parent chain — walk up to the first untrusted ancestor
time / tickTime
What does the timeline look like?
First event, 3rd file, encryption duration
18.2 — False positive triage decisions
Observed
Assessment
Decision
Process is under C:\Windows\ or Program Files
S1 would already have dropped it — probably a different alert
Do not triage
Target paths are under AppData
S2/S4 would already have dropped it — probably a cache/application operation
Do not triage
Targets are under C:\Users\, text files
S8 may have let it through — could be a document editor / sync tool
Check the process signature and cmdLine
Process is signed by a trusted organization, verdict SAFE
The trust gate rule already applied (there should be no kill)
May be an integration test; log it
Process is unsigned, in %TEMP%, high operation counter
Classic first run profile
High priority — verify the restore
A winword.exe / excel.exe appears in the processes[] chain
Could be a document macro or COM hijacking
High priority; analyze the macro
18.3 — If you want to change the rule
The rule is not externally configurable; source code modifications
carry a risk of introducing vulnerabilities. The cleanest approach is to fork the policy file and
edit the RANSOM_BEHAVIOR block in
edrdata/ptm.local.src. The most commonly needed changes:
COMMON TUNING POINTS4 LINES
// 1) Sensitivity / tolerance tuning (default 3/60s)
"threshold": 3, // set to 2 to also catch test samples
"window": 60000, // set to 300000 to also catch slow encryptors// 2) Location tuning (required positive condition)
"pattern": "*\\users\\*" // create a separate rule for network drives// 3) Yeni korunacak yol eklemek"ransomSystemExclusions": [ ..., "*\\OneDrive\\*", "*\\Google\\Drive\\*" ]
▶ Warning
In the ransomSystemExclusions list, adding a broad path
(such as *\OneDrive\*) also hides real ransomware encryption
in that folder. Because corporate cloud sync folders are real targets,
such extensions must be made with care.
19 Sık Sorulan Sorular
01Is this rule a YARA rule?
No. It is a PTM v2 policy rule (JSON), evaluated in the C++
engine. In the Viruskov project, YARA × runs separately and complements it:
YARA looks at file content, while MLE_RANSOM_BEHAVIOR looks at
behavior. Together they provide both static and dynamic coverage.
02Is malware that encrypts a single file caught?
No. The S9 filter requires at least 3 distinct targets within 60 seconds.
This is a deliberate design decision: encrypting a single file is usually user or
backup software behavior. If you want the rule to fire on a single file, you can try
threshold: 1 — but the false positive rate rises sharply.
03What about malware that encrypts only text files?
No, that is out of scope. S8 (!equal(isAsciiText, true))
deliberately lets text files through. Reason: corporate email encryption, document
processing software and sync clients constantly write text files;
covering them would make the FP rate unusable.
This is a deliberate FP/recall trade-off.
04I already have an antivirus, why do I need this?
Three concrete reasons: (1) Commercial AVs run in user mode;
they can be wiped from memory in one go with BYOVD — this rule detects
while that is already happening. (2) Without power or network connectivity, whitelists cannot be updated;
this rule needs no external signatures. (3) It produces an
evidence-grade timeline for insurance, audit and incident
response (DFIR).
05What happens to my data, is it really restored?
Yes, as long as the file system still holds the original data on disk.
Before an in-place write to a file, the minifilter saves a pre-image of the original
content under %PROGRAMDATA%\HydraDragonBackups during the preSetFileInfo
stage. When the MLE fires, EventEnricher::rollbackRansomBackups()
restores these copies.
Important limit: Restore succeeds only for files for which
a pre-image could be taken. If the encryptor uses the “read first, create a new encrypted file, then
delete the original” model, the minifilter still takes the pre-image
(the “generic guard against every ransomware family” comment states this explicitly in the code),
but data fully erased from disk cannot be recovered.
06What if the rule is triggered by trusted software?
There are two layers of protection. (1) The S1/S2 whitelist, (2) the
should_trust_company_whitelist and
should_trust_comodo_fls_cloud trust gate. If both apply, the
process is not killed or quarantined; only an alert is logged. However, if
owlyshield_is_malicious_company_signer identifies a malicious/PUA vendor,
the trust flags are overridden and strict enforcement takes effect.
07Can it be demonstrated with a real sample?
Yes — a live detection recording of the rule has been shared via a zero-day
sample on YouTube.
Important: Do not run ransomware samples
on your own machine. Deliberately infecting a system
can have legal consequences. This project offers no technical support for running samples
outside a container / virtual environment — do not run integration tests.
08Who wrote this rule, and for which process?
The rule was developed on a fork of OpenEDR (the EDR core open-sourced by COMODO)
as part of the VIRUSKOV / Hydra Dragon project.
Project lead: Emirhan Uçan. All source code is in the
public repository;
every claim on this page is taken directly from that code.
20 Kaynak Haritası
The source file for every claim on this page. Because the policy engine is
open source, everything you read can be verified directly.
16 (3 of them duplicate / dead code — see Section 16)
Threshold
3 distinct targets / 60000 ms sliding window
Confidence Level
3 — CRITICAL
Response
Quarantine (attacker binary) + kill by GID + pre-image restore
License
Open source (OpenEDR fork: BSD-3 / VirusKat contributions)
RANSOM_BEHAVIOR
MLE_RANSOM_CANDIDATE
MLE_RANSOM_EVALUATE
distinctCounter
saveContext
createEvent
imatch
StringMatcher
Shannon Entropy Q24
minifilter
IRP_MJ_ACQUIRE_FOR_SECTION_SYNCHRONIZATION
isAsciiText
RansomShield
OwlyShield
Ring-0
PTM v2
EDR
anti-ransomware
✦ Support, Contributions and Donations
This wiki and the MLE_RANSOM_BEHAVIOR documentation are part of the project's open source
philosophy: nothing is hidden. You have read this rule,
examined its source code, and even seen the
known flaws on this page.
If you want to contribute to the project, write additional rules, or simply offer financial support
(server / test environment / code signing certificate costs):
[email protected]. All requests go directly to the project lead —
no intermediaries, no commissions; just as you see what is inside the box on these pages,
you will see it in the donation flow too.