
دليل حول كيفية كتابة قواعد YARA سريعة وصديقة للذاكرة
كتابة قواعد YARA فعّالة أمر أساسي للحفاظ على سرعة ودقة الأداء عند الفحص. يوفّر هذا الدليل مبادئ رئيسية وأفضل الممارسات التي تساعدك على تحسين قواعدك، وتقليل الحسابات غير الضرورية، وتجنّب الأخطاء الشائعة. يتضمن الدليل رؤى من خبراء في المجال، منهم Victor M. Alvarez وWXS، ومساهمات من مجتمع YARA.
يوفر هذا القسم ملخصًا موجزًا لأفضل ممارسات أداء YARA. للحصول على شروحات مفصّلة وأمثلة، راجع الدليل الكامل أدناه.
"فكّر في YARA كعملية من خطوتين: أولاً، البحث عن جميع الأنماط المدرجة في السلاسل، وثانيًا، تقييم الشروط. لا يمكنك استخدام شروط جيدة الصياغة للتعويض عن سلاسل سيئة الاختيار."
— Wesley Shields
يتبع YARA أربع خطوات رئيسية عند فحص ملف:
يبحث YARA عن السلاسل أولاً، مما يجعل اختيار السلاسل العامل الأكثر أهمية في كفاءة القاعدة.
✅ أفضل الممارسات للسلاسل:
\x00\x00\x00\x00 يظهر بشكل متكرر جدًا.nocase بحذر – فهو يولّد عددًا هائلًا من Variations البحث.يقوم YARA بتقييم الشروط تسلسليًا ويتوقف عند أول فشل.
✅ أفضل الممارسات للشروط:
filesize < X) قبل الشروط المكلفة.for all i in (1..filesize) غير فعّال).@) بدلاً من التعبيرات النمطية لفحص التسلسل.⚠ ملاحظة: شروط التعبيرات النمطية لا تخضع للاختصار المنطقي ويتم تقييمها دائمًا في النهاية.
الوحدات مثل pe أو elf أو magic يجب أن تحلل الملف بالكامل قبل التقييم، مما يزيد من زمن الفحص.
✅ بدائل:
pe.is_pe، استخدم uint16(0) == 0x5A4D لتحديد ملفات PE.التطابقات المفرطة تبطئ الفحص وقد تؤدي إلى أخطاء "too many matches".
✅ إصلاح التطابقات غير الفعّالة:
.* أو .+ أو {x,} بدون حد أعلى.أعدّ @herrcore فيديو تعليميًا مفيدًا يغطي الموضوعات التي تمت مناقشتها في دليل الأداء هذا.
مقدمة إلى YARA - كتابة قواعد YARA فعّالة
لفهم أين وكيف يمكن تحسين أداء 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 عن ما يسمى atoms (ذرات) في سلاسل البحث لتغذية آلية Aho-Corasick. التفاصيل موضحة في فصل الذرات (atoms)، لكن يكفي الآن معرفة أن طولها يبلغ 4 بايت كحد أقصى، وأن YARA يختارها بذكاء لتجنّب كثرة التطابقات. في مثالنا، قد يختار YARA الذرات الأربع التالية:
<?phGETPOSTsser (من assert)هنا يبدأ الفحص. سيتم تنفيذ الخطوات 2-4 على جميع الملفات. سيبحث YARA في كل ملف عن الذرات الأربع المعرّفة أعلاه باستخدام شجرة بادئات تُسمى آلية Aho-Corasick. يتم تمرير أي تطابقات إلى محرك البايت كود.
إذا كان هناك مثلًا تطابق على sser، فسيتحقق YARA مما إذا كانت مسبوقة بحرف a ويستمر بحرف t. إذا كان ذلك صحيحًا، سيتابع مع التعبير النمطي [\t ]{0,100}\(. بهذا الأسلوب الذكي، يتجنب YARA استخدام محرك تعبيرات نمطية بطيء على الملفات الكاملة، ويختار فقط أجزاء معينة لفحصها عن قرب.
بعد اكتمال جميع مطابقة الأنماط، يتم التحقق من الشروط.
لدى YARA آلية تحسين أخرى لتنفيذ فحص math.entropy المُكلف من قاعدة المثال فقط إذا كانت الشروط الأربعة التي تسبقه مستوفاة. شرح ذلك بمزيد من التفصيل في فصل الشروط والتقييم الاختصاري
إذا كانت الشروط مستوفاة، يتم الإبلاغ عن تطابق. يستمر الفحص مع الملف التالي في الخطوة 2.
يستخرج YARA من السلاسل سلاسل فرعية قصيرة يصل طولها إلى 4 بايت تُسمى "ذرات" (atoms). يمكن استخراج هذه الذرات من أي مكان داخل السلسلة، ويبحث 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/
هذه التعبيرات النمطية لا تحتوي على أي سلسلة فرعية ثابتة يمكن استخدامها كذرة، لذلك يجب تقييمها عند كل إزاحة في الملف لمعرفة ما إذا كانت تطابق هناك.
توصية جيدة أخرى هي تجنّب الحلقات (loops) ذات التكرارات الكثيرة جدًا، خاصة إذا كانت العبارة داخل الحلقة معقدة جدًا، على سبيل المثال:
strings:
$a = {00 00}
condition:
for all i in (1..#a) : (@a[i] < 10000)
هذه القاعدة فيها مشكلتان. الأولى أن السلسلة $a شائعة جدًا، والثانية أنه بسبب شيوع $a فإن #a يمكن أن يكون كبيرًا جدًا وقد يتم تقييمه آلاف المرات.
هذا الشرط الآخر غير فعّال أيضًا لأن عدد التكرارات يعتمد على حجم الملف، والذي يمكن أن يكون كبيرًا جدًا أيضًا:
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 بايت ستظهر غالبًا في الكثير من الملفات أو كمحتوى موحّد في ملف مُعامل بعملية XOR.