
तेज़ और मेमोरी-अनुकूल YARA नियम लिखने के तरीके पर एक मार्गदर्शिका
कुशल YARA नियम लिखना तेज़ और सटीक स्कैनिंग प्रदर्शन बनाए रखने के लिए आवश्यक है। यह मार्गदर्शिका आपके नियमों को अनुकूलित करने, अनावश्यक गणना कम करने और सामान्य गलतियों से बचने में मदद करने के लिए प्रमुख सिद्धांत और सर्वोत्तम अभ्यास प्रदान करती है। इसमें उद्योग विशेषज्ञों, जिनमें Victor M. Alvarez, WXS, और YARA समुदाय के योगदान शामिल हैं, की अंतर्दृष्टि शामिल है।
यह अनुभाग YARA प्रदर्शन सर्वोत्तम अभ्यासों का संक्षिप्त सारांश प्रदान करता है। विस्तृत स्पष्टीकरण और उदाहरणों के लिए, नीचे दिए गए पूर्ण मार्गदर्शिका देखें।
"YARA को दो-चरणीय प्रक्रिया के रूप में सोचें: पहले, स्ट्रिंग्स में सूचीबद्ध सभी पैटर्न की खोज करना, और दूसरे, कंडीशन का मूल्यांकन करना। आप खराब चुनी गई स्ट्रिंग्स की भरपाई के लिए सुव्यवस्थित कंडीशन का उपयोग नहीं कर सकते।"
— Wesley Shields
YARA फ़ाइल स्कैन करते समय चार मुख्य चरणों का पालन करता है:
YARA पहले स्ट्रिंग्स की खोज करता है, जिससे स्ट्रिंग चयन नियम दक्षता के लिए सबसे महत्वपूर्ण एकल कारक बन जाता है।
✅ स्ट्रिंग्स के लिए सर्वोत्तम अभ्यास:
\x00\x00\x00\x00 बहुत बार आता है।nocase का सावधानी से उपयोग करें – यह खोज भिन्नताओं को तेज़ी से बढ़ाता है।YARA कंडीशन का क्रमिक मूल्यांकन करता है और पहली विफलता पर रुक जाता है।
✅ कंडीशन के लिए सर्वोत्तम अभ्यास:
filesize < X) महंगी कंडीशन से पहले।for all i in (1..filesize) अक्षम है)।@) का उपयोग करें।⚠ ध्यान दें: रेगेक्स कंडीशन शॉर्ट-सर्किट नहीं होती हैं और हमेशा अंत में मूल्यांकित की जाती हैं।
pe, elf, या magic जैसे मॉड्यूल को मूल्यांकन से पहले पूरी फ़ाइल को पार्स करना चाहिए, जिससे स्कैन समय बढ़ता है।
✅ विकल्प:
pe.is_pe के बजाय, PE फ़ाइलों की पहचान करने के लिए uint16(0) == 0x5A4D का उपयोग करें।अत्यधिक मैच स्कैनिंग को धीमा कर देते हैं और "too many matches" त्रुटियाँ उत्पन्न कर सकते हैं।
✅ अक्षम मैचों को ठीक करना:
.*, .+, या {x,} से बचें।@herrcore ने इस प्रदर्शन मार्गदर्शिका में चर्चित विषयों को कवर करते हुए एक सहायक वीडियो ट्यूटोरियल बनाया है।
Introduction Into YARA - Writing Efficient YARA Rules
इस बात की बेहतर समझ पाने के लिए कि YARA प्रदर्शन को कहाँ और कैसे अनुकूलित किया जा सकता है, स्कैनिंग प्रक्रिया को समझना उपयोगी है। यह मूल रूप से 4 चरणों में विभाजित है, जिन्हें इस उदाहरण नियम का उपयोग करके बहुत सरल रूप से समझाया जाएगा:
import "math"
rule example_php_webshell_rule
{
meta:
description = "Just an example php webshell rule"
date = "2021/02/16"
strings:
$php_tag = "<?php"
$input1 = "GET"
$input2 = "POST"
$payload = /assert[\t ]{0,100}\(/
condition:
filesize < 20KB and
$php_tag and
$payload and
any of ( $input* ) and
math.entropy(500, filesize-500) >= 5
}
यह चरण वास्तविक स्कैन से पहले होता है। YARA Aho-Corasick ऑटोमेटन को फीड करने के लिए खोज स्ट्रिंग्स में तथाकथित एटम देखेगा। विवरण एटम अध्याय में समझाए गए हैं, लेकिन अभी के लिए यह जानना पर्याप्त है कि वे अधिकतम 4 बाइट लंबे होते हैं और YARA बहुत अधिक मैचों से बचने के लिए उन्हें काफी समझदारी से चुनता है। हमारे उदाहरण में YARA निम्नलिखित 4 एटम चुन सकता है:
<?phGETPOSTsser (assert में से)यहाँ स्कैन शुरू हो गया है। चरण 2.-4. सभी फ़ाइलों पर निष्पादित किए जाएंगे। YARA प्रत्येक फ़ाइल में ऊपर परिभाषित 4 एटम को Aho-Corasick ऑटोमेटन नामक प्रीफिक्स ट्री के साथ देखेगा। किसी भी मैच को बाइटकोड इंजन को सौंप दिया जाता है।
यदि, उदाहरण के लिए, sser पर मैच होता है, तो YARA जाँच करेगा कि क्या इसके पहले a था और फिर t के साथ आगे बढ़ता है। यदि यह सत्य है, तो यह रेगेक्स [\t ]{0,100}\( के साथ आगे बढ़ेगा। इस समझदार दृष्टिकोण से YARA धीमे रेगेक्स इंजन के साथ पूरी फ़ाइलों से गुजरने से बचता है और केवल कुछ विशेष भागों को चुनकर करीब से देखता है।
सभी पैटर्न मिलान पूरा होने के बाद, कंडीशन की जाँच की जाती है।
YARA के पास एक और अनुकूलन तंत्र है, जिससे हमारे उदाहरण नियम से केवल CPU-गहन math.entropy जाँच तभी की जाती है, जब उससे पहले की 4 कंडीशन संतुष्ट हों। इसके बारे में अधिक विवरण कंडीशन और शॉर्ट-सर्किट मूल्यांकन अध्याय में समझाया गया है।
यदि कंडीशन संतुष्ट होती हैं, तो मैच की सूचना दी जाती है। स्कैन चरण 2 में अगली फ़ाइल के साथ जारी रहता है।
YARA स्ट्रिंग्स से 4 बाइट तक लंबे छोटे सबस्ट्रिंग निकालता है जिन्हें "एटम" कहा जाता है। वे एटम स्ट्रिंग के किसी भी स्थान से निकाले जा सकते हैं, और YARA फ़ाइल स्कैन करते समय उन एटम को खोजता है, यदि उसे कोई एटम मिलता है तो वह सत्यापित करता है कि स्ट्रिंग वास्तव में मेल खाती है या नहीं।
उदाहरण के लिए, इन स्ट्रिंग्स पर विचार करें:
/abc.*cde/
=> संभावित एटम abc और cde हैं, उनमें से किसी एक का उपयोग किया जा सकता है। वर्तमान में abc एटम को प्राथमिकता दी जाती है क्योंकि उनकी गुणवत्ता समान है और यह दोनों में से पहला है।
/(one|two)three/
=> संभावित एटम one, two, thre और hree हैं, हम केवल thre (या hree) खोज सकते हैं, या one और two दोनों खोज सकते हैं। एटम thre को प्राथमिकता दी जाती है क्योंकि यह one और two (जो छोटे हैं) की तुलना में कम संभावित मैच लाएगा और इसमें दोहरा e नहीं है (अक्षर जितना अधिक अद्वितीय, उतना बेहतर)।
YARA प्रत्येक स्ट्रिंग से सर्वोत्तम एटम चुनने का पूरा प्रयास करता है, उदाहरण के लिए:
{ 00 00 00 00 [1-4] 01 02 03 04 }
=> यहाँ YARA एटम 01 02 03 04 का उपयोग करता है, क्योंकि 00 00 00 00 बहुत सामान्य है
{ 01 02 [1-4] 01 02 03 04 }
=> 01 02 03 04 को 01 02 पर प्राथमिकता दी जाती है क्योंकि यह लंबा है
इसलिए, महत्वपूर्ण बिंदु यह है कि स्ट्रिंग्स में अच्छे एटम होने चाहिए। ये खराब स्ट्रिंग्स हैं क्योंकि इनमें या तो बहुत छोटे या बहुत सामान्य एटम होते हैं:
{00 00 00 00 [1-2] FF FF [1-2] 00 00 00 00}
{AB [1-2] 03 21 [1-2] 01 02}
/a.*b/
/a(c|d)/
सबसे खराब स्ट्रिंग्स वे हैं जिनमें कोई भी एटम नहीं होता, जैसे:
/\w.*\d/
/[0-9]+\n/
इस रेगेक्स में कोई भी निश्चित सबस्ट्रिंग नहीं है जिसे एटम के रूप में उपयोग किया जा सके, इसलिए यह देखने के लिए कि क्या यह वहाँ मेल खाता है, फ़ाइल के हर ऑफसेट पर इसका मूल्यांकन किया जाना चाहिए।
एक और अच्छी अनुशंसा यह है कि लूप में बहुत अधिक पुनरावृत्तियों से बचें, विशेष रूप से यदि लूप के भीतर का कथन बहुत जटिल है, उदाहरण के लिए:
strings:
$a = {00 00}
condition:
for all i in (1..#a) : (@a[i] < 10000)
इस नियम में दो समस्याएँ हैं। पहली यह कि स्ट्रिंग $a बहुत सामान्य है, दूसरी यह कि $a बहुत सामान्य होने के कारण #a बहुत अधिक हो सकता है और इसका हजारों बार मूल्यांकन किया जा सकता है।
यह अन्य कंडीशन भी अक्षम है क्योंकि पुनरावृत्तियों की संख्या filesize पर निर्भर करती है, जो बहुत अधिक भी हो सकती है:
for all i in (1..filesize) : ($a at i)
"magic" मॉड्यूल का उपयोग करने से बचें, जो Windows प्लेटफ़ॉर्म पर उपलब्ध नहीं है। "magic" मॉड्यूल का उपयोग स्कैनिंग को धीमा कर देता है लेकिन सटीक मैच प्रदान करता है।
कस्टम GIF मैजिक हेडर परिभाषा:
rule gif_1 {
condition:
(uint32be(0) == 0x47494638 and uint16be(4) == 0x3961) or
(uint32be(0) == 0x47494638 and uint16be(4) == 0x3761)
}
"magic" मॉड्यूल का उपयोग करना:
import "magic"
rule gif_2 {
condition:
magic.mime_type() == "image/gif"
}
बहुत छोटी स्ट्रिंग्स परिभाषित करने से बचें। 4 बाइट्स से कम वाली कोई भी स्ट्रिंग संभवतः बहुत सारी फ़ाइलों में या XORed फ़ाइल में समान सामग्री के रूप में दिखाई देगी।
कुछ स्ट्रिंग्स काफी लंबी होती हैं लेकिन एक अलग कारण - एकरूपता - के कारण उनका उपयोग नहीं किया जाना चाहिए। ये कुछ उदाहरण हैं स्ट्रिंग्स के जिनका उपयोग नहीं किया जाना चाहिए क्योंकि वे फ़ाइलों में बहुत अधिक मैच उत्पन्न कर सकती हैं।
$s1 = "22222222222222222222222222222222222222222222222222222222222222"
$s2 = "\x00\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00\x20\x00\x20" // wide formatted spaces
त्रुटि संदेश कुछ ऐसा दिखेगा:
error scanning yara-killer.dat: string "$mz" in rule "shitty_mz" caused too many matches
स्ट्रिंग परिभाषाओं को यथासंभव संकीर्ण/सीमित रूप में वर्णित करने का प्रयास करें। यदि संभव हो तो "nocase" विशेषता से बचें, क्योंकि कई एटम उत्पन्न और खोजे जाएंगे (उच्च मेमोरी उपयोग, अधिक पुनरावृत्तियाँ)। याद रखें, संशोधकों की अनुपस्थिति में डिफ़ॉल्ट रूप से "ascii" माना जाता है। संभावित संयोजन हैं:
कम - केवल एक एटम उत्पन्न होता है
$s1 = "cmd.exe" // (ascii only)
$s2 = "cmd.exe" ascii // (ascii only, same as $s1)
$s3 = "cmd.exe" wide // (UTF-16 only)
$s4 = "cmd.exe" ascii wide // (both ascii and UTF-16) two atoms will be generated
$s5 = { 63 6d 64 2e 65 78 65 } // ascii char code in hex
उच्च - YARA द्वारा चुने गए 4 बाइट्स के लिए ऊपरी और निचले अक्षरों के सभी संयोजन एटम के रूप में उत्पन्न होंगे
$s5 = "cmd.exe" nocase (all different cases, e.g. "Cmd.", "cMd.", "cmD." ..)
यदि आप स्क्रिप्टिंग कमांड से मेल खाना चाहते हैं, तो nocase का उपयोग करने से पहले जाँच लें कि भाषा बिल्कुल केस-इनसेंसिटिव है या नहीं (जैसे php, Windows batch)। यदि आपको केवल एक या दो अक्षरों के लिए अलग केस चाहिए, तो रेगेक्स का उपयोग करना बेहतर है, उदाहरण के लिए
$re = /[Pp]assword/
वैकल्पिकता (alternation) के साथ काम करते समय सावधान रहें जैसे:
$re = /(a|b)cde/
$hex = {C7 C3 00 (31 | 33)}
ये स्ट्रिंग्स छोटे एटम उत्पन्न करती हैं जो स्कैनिंग को धीमा कर सकते हैं। ऐसे मामलों में जहाँ विकल्पों की संख्या कम है, स्ट्रिंग को अलग-अलग लिखने की अनुशंसा की जाती है:
$re1 = /acde/
$re2 = /bcde/
$hex1 = {C7 C3 00 31}
$hex2 = {C7 C3 00 33}
रेगुलर एक्सप्रेशन का उपयोग केवल तभी करें जब आवश्यक हो। रेगुलर एक्सप्रेशन मूल्यांकन सरल स्ट्रिंग मिलान की तुलना में स्वाभाविक रूप से धीमा है और महत्वपूर्ण मात्रा में मेमोरी की खपत करता है। यदि जंप और वाइल्ड-कार्ड वाली हेक्स स्ट्रिंग्स समस्या हल कर सकती हैं तो उनका उपयोग न करें।
यदि आपको रेगुलर एक्सप्रेशन का उपयोग करना ही है तो लालची .* और यहाँ तक कि अनिच्छुक क्वांटिफायर .*? से बचें। इसके बजाय सटीक संख्याओं का उपयोग करें जैसे .{1,30} या यहाँ तक .{1,3000}। साथ ही, ऊपरी सीमा को न भूलें (जैसे .{2,} से बचें)।
जब हम क्वांटिफायर का उपयोग कर रहे होते हैं, तो दो स्थितियाँ हो सकती हैं:
यदि रेगुलर एक्सप्रेशन की शुरुआत एक स्थिति पर एंकर है और केवल प्रत्यय बदल सकता है, तो YARA सबसे लंबे संभव मैच का मिलान करेगा। .* और .+ या .{2,} जैसे मामलों में, यह बड़ी स्ट्रिंग्स और स्कैनिंग धीमी होने की समस्याएँ पैदा कर सकता है।
यदि रेगुलर एक्सप्रेशन की अधिक संभावित शुरुआतें हैं, तो YARA उन सभी का मिलान करेगा।
$re1 = /Tom.{0,2}/ // will find Tomxx in "Tomxx"
$re2 = /.{0,2}Tom/ // will find Tom, xTom, xxTom in "xxTom"
छोटे मैचों की संख्या आसानी से सीमा पार कर सकती है और "too many matches" त्रुटि उत्पन्न कर सकती है।
निम्नलिखित उदाहरण एक ई-मेल पते के लिए रेगुलर एक्सप्रेशन है। क्वांटिफायर के साथ [-a-z0-9._%+] का उपयोग करते समय, YARA एक पते का कई बार मिलान करेगा, जो आदर्श नहीं है। इस मामले में, विश्लेषण के लिए पर्याप्त जानकारी प्रदान करने वाले पतों का एक उचित छोटा सबसेट खोजने की अनुशंसा की जाती है।
उपयोग करें
/[-a-z0-9._%+]@[-a-z0-9.]{2,10}\.[a-z]{2,4}/
OR
/@[-a-z0-9.]{2,10}\.[a-z]{2,4}/
बचें
/[-a-z0-9._%+]*@[-a-z0-9.]{2,10}\.[a-z]{2,4}/
/[-a-z0-9._%+]+@[-a-z0-9.]{2,10}\.[a-z]{2,4}/
/[-a-z0-9._%+]{x,y}@[-a-z0-9.]{2,10}\.[a-z]{2,4}/
यदि आप यह सुनिश्चित करना चाहते हैं कि, उदाहरण के लिए, exec के बाद /bin/sh आता है, तो आप @ प्रतीक द्वारा प्रदान किए गए ऑफसेट का उपयोग कर सकते हैं। यह धीमा रेगेक्स संस्करण होगा:
$ = /exec.*\/bin\/sh/
यह तेज़ ऑफसेट तरीका है:
strings:
$exec = "exec"
$sh = "/bin/sh"
conditions:
$exec and $sh and
@exec < @sh
साथ ही, स्ट्रिंग्स के लंबे अनुक्रम शामिल करने का प्रयास करें जो मिलान प्रक्रिया में एंकर के रूप में काम कर सकें। फिर से, जितना लंबा, उतना बेहतर।
खराब
$s1 = /http:\/\/[.]*\.hta/ // greedy [.]*
बेहतर
$s1 = /http:\/\/[a-z0-9\.\/]{3,70}\.hta/ // better, with an the upper bound
सर्वोत्तम
$s1 = /mshta\.exe http:\/\/[a-z0-9\.\/]{3,70}\.hta/
बहुत अधिक मैच त्रुटियाँ बहुत सामान्य स्ट्रिंग्स के कारण होती हैं जो इनपुट में बहुत बार मौजूद होती हैं, या YARA एक उदाहरण का कई बार मिलान कर रहा है।
स्कैनिंग का धीमा होना उन स्ट्रिंग्स के कारण होता है जो बहुत छोटे एटम उत्पन्न कर रही हैं, या बिल्कुल भी नहीं। परिणामस्वरूप, YARA एक भोले-भाले पैटर्न मिलान एल्गोरिथ्म का उपयोग करता है, जो मंदी का कारण बनता है।
इन दोनों समस्याओं को, कुछ मामलों में, इन चरणों द्वारा ठीक किया जा सकता है:
.* और .+, .*? की जाँच करेंx{14,} की जाँच करेंध्यान दें, अगले अध्याय कंडीशन और शॉर्ट-सर्किट मूल्यांकन में, कंडीशन के लिए कुछ युक्तियों का उल्लेख किया गया है। हालाँकि, उनमें परिवर्तन बहुत अधिक मैच और स्कैनिंग धीमी होने की त्रुटियों को हल नहीं करेंगे।
कंडीशन स्टेटमेंट लिखने का प्रयास करें जिसमें वे तत्व जिनके "False" होने की सबसे अधिक संभावना है, पहले रखे जाएँ। कंडीशन का मूल्यांकन बाएँ से दाएँ किया जाता है। जितनी जल्दी इंजन पहचानता है कि एक नियम संतुष्ट नहीं है, उतनी जल्दी वह वर्तमान नियम को छोड़ सकता है और अगले का मूल्यांकन कर सकता है। कंडीशन स्टेटमेंट को इस तरह क्रमबद्ध करने से होने वाला गति सुधार प्रत्येक स्टेटमेंट को संसाधित करने के लिए आवश्यक CPU चक्रों में अंतर पर निर्भर करता है। यदि सभी स्टेटमेंट लगभग समान रूप से महंगे हैं, तो स्टेटमेंट को पुनः क्रमबद्ध करने से कोई ध्यान देने योग्य सुधार नहीं होता। यदि स्टेकमेंट में से एक को बहुत तेज़ी से संसाधित किया जा सकता है, तो उसे पहले रखने की अनुशंसा की जाती है ताकि पहले स्टेटमेंट के FALSE होने की स्थिति में महंगे स्टेटमेंट मूल्यांकन को छोड़ा जा सके।
निम्नलिखित कथन में क्रम बदलने से कोई महत्वपूर्ण सुधार नहीं होता:
$string1 and $string2 and uint16(0) == 0x5A4D
हालाँकि, यदि स्टेटमेंट के निष्पादन समय बहुत भिन्न हैं, तो शॉर्ट-सर्किट को ट्रिगर करने के लिए पुनः क्रमबद्ध करने से स्कैन गति में काफी सुधार होगा:
धीमा
// EXPENSIVE और CHEAP
math.entropy(0, filesize) > 7.0 and uint16(0) == 0x5A4D
तेज़
// CHEAP और EXPENSIVE
uint16(0) == 0x5A4D and math.entropy(0, filesize) > 7.0
शॉर्ट-सर्किट मूल्यांकन महंगे कथनों, विशेष रूप से "for" कथनों को अनुकूलित करने में मदद करने के लिए पेश किया गया था। कुछ लोग निम्नलिखित उदाहरण जैसी कंडीशन का उपयोग कर रहे थे:
strings:
$mz = "MZ"
...
condition:
$mz at 0 and for all i in (1..filesize) : ( whatever )
क्योंकि filesize बहुत बड़ी संख्या हो सकती है, "whatever" कई बार निष्पादित किया जा सकता है, जिससे निष्पादन धीमा हो जाता है। अब, शॉर्ट-सर्किट मूल्यांकन के साथ, "for" कथन केवल तभी निष्पादित किया जाएगा जब कंडीशन का पहला भाग पूरा होगा, इसलिए, यह नियम केवल MZ फ़ाइलों के लिए धीमा होगा। एक अतिरिक्त सुधार हो सकता है:
$mz at 0 and filesize < 100KB and for all i in (1..filesize) : ( whatever )
इस तरह पुनरावृत्तियों की संख्या के लिए एक उच्च सीमा निर्धारित की जाती है।
संस्करण 3.10 से, पूर्णांक रेंज लूप भी अनुकूलित किए गए थे:
for all i in (0..100): (false)
for any i in (0..100): (true)
Both of these loops will stop iterating after the first time through.
दुर्भाग्य से यह रेगुलर एक्सप्रेशन के साथ काम नहीं करता क्योंकि वे सभी शुरू में स्ट्रिंग मिलान इंजन में फीड किए जाते हैं। निम्नलिखित उदाहरण किसी भी फ़ाइल के लिए खोज को धीमा कर देगा, न कि केवल उन फ़ाइलों के लिए जिनका आकार 200 बाइट्स से कम है:
strings:
$expensive_regex = /\$[a-z0-9_]+\(/ nocase
conditions:
filesize < 200 and
$expensive_regex
यह "शॉर्ट-सर्किट" मूल्यांकन YARA संस्करण 3.4 से लागू किया गया है।
मेटाडेटा अनुभाग में कोई भी डेटा YARA द्वारा RAM में पढ़ा जाता है। (आप एक नियम में 100,000 हैश डालकर और स्कैन से पहले और बाद में YARA स्कैन की RAM उपयोग की जाँच करके इसे आसानी से परीक्षण कर सकते हैं।) बेशक आप नियमों से मेटाडेटा को स्थायी रूप से नहीं हटाना चाहते, लेकिन यदि आपके पास RAM की कमी है, तो आप अपने वर्कफ़्लो में स्कैनिंग से ठीक पहले इसके कुछ अनावश्यक भागों को हटा सकते हैं।