
إضافة لـ IDA Pro للبحث الاستعلامي داخل الملف الثنائي، مفيدة بشكل أساسي لأبحاث الثغرات الأمنية.
أداة VulFi (Vulnerability Finder) هي إضافة لـ IDA Pro يمكن استخدامها للمساعدة في صيد الثغرات في الملفات الثنائية. هدفها الرئيسي هو توفير عرض واحد يحتوي على جميع المراجع المتبادلة لأكثر الدوال إثارة للاهتمام (مثل strcpy و sprintf و system وغيرها). في الحالات التي يمكن فيها استخدام مفكك Hexrays، ستحاول استبعاد استدعاءات هذه الدوال غير المثيرة للاهتمام من منظور البحث عن الثغرات (فكر في شيء مثل strcpy(dst,"Hello World!")). بدون المفكك، تكون القواعد أبسط بكثير (لعدم الاعتماد على بنية المعالج) وبالتالي تستبعد فقط الحالات الأكثر وضوحًا.
انظر سجل التغييرات لمزيد من المعلومات حول التحديثات.
ضع الملفات vulfi.py و vulfi_prototypes.json و vulfi_rules.json في مجلد إضافات IDA (cp vulfi* <IDA_PLUGIN_FOLDER>).
قبل تشغيل VulFi تأكد من أن لديك فهمًا جيدًا للملف الثنائي الذي تعمل معه. حاول تحديد جميع الدوال القياسية (strcpy، memcpy، إلخ) وتسميتها وفقًا لذلك. الإضافة لا تفرق بين الأحرف الكبيرة والصغيرة وبالتالي فإن MEMCPY و Memcpy و memcpy كلها أسماء صالحة. ومع ذلك، لاحظ أن البحث عن الدالة يتطلب تطابقًا تامًا. هذا يعني أن memcpy? أو std_memcpy (أو أي متغير آخر) لن يتم اكتشافه كدالة قياسية وبالتالي لن يتم أخذه في الاعتبار عند البحث عن ثغرات محتملة. إذا كنت تعمل مع ملف ثنائي غير معروف، فيجب عليك أولاً ضبط خيارات المترجم Options > Compiler. بعد ذلك سيبذل VulFi قصارى جهده لتصفية جميع الإيجابيات الكاذبة الواضحة (مثل استدعاء printf بسلسلة ثابتة كمعامل أول). يرجى ملاحظة أنه على الرغم من أن الإضافة مصنوعة دون أي ارتباط ببنية معينة، إلا أن بعض المعالجات لا تدعم بشكل كامل تحديد الأنواع، وفي هذه الحالة سيقوم VulFi ببساطة بوضع علامة على جميع المراجع المتبادلة للدوال القياسية التي قد تكون خطيرة للسماح لك بالمتابعة مع التحليل اليدوي. في هذه الحالات، يمكنك الاستفادة من ميزات التتبع في الإضافة.
لبدء المسح، حدد خيار Search > VulFi من شريط القائمة العلوي. سيؤدي ذلك إما إلى بدء مسح جديد، أو قراءة النتائج السابقة المخزنة داخل ملف idb/i64. يتم حفظ البيانات تلقائيًا كلما قمت بحفظ قاعدة البيانات.
بمجرد اكتمال المسح أو تحميل النتائج السابقة، سيتم عرض جدول بعرض يحتوي على الأعمدة التالية:
Not Checked الأولي لكل عنصر جديد. الحالات الأخرى هي False Positive و Suspicious و Vulnerable. يمكن تعيينها باستخدام قائمة النقر بزر الماوس الأيمن على عنصر معين ويجب أن تعكس نتائج المراجعة اليدوية لاستدعاء الدالة المحدد.High و Medium و Low (أيضًا Info للحالات التي لم يتمكن الماسح من تحديد جميع المعاملات بشكل صحيح). يتم تعريف الأولويات جنبًا إلى جنب مع القواعد الأخرى في ملف vulfi_rules.json.في حالة عدم وجود بيانات داخل ملف idb/i64 أو قرر المستخدم إجراء مسح جديد. ستسأل الإضافة عما إذا كان يجب تشغيل المسح باستخدام القواعد المضمنة الافتراضية أو استخدام ملف قواعد مخصص. يرجى ملاحظة أن تشغيل مسح جديد ببيانات موجودة بالفعل لا يستبدل العناصر المكتشفة سابقًا التي تم تحديدها بواسطة قاعدة بنفس اسم القاعدة التي تحتوي على النتائج المخزنة سابقًا. لذلك، فإن إعادة تشغيل المسح لا يحذف التعليقات الحالية أو تحديثات الحالة.

في قائمة السياق للنقر بزر الماوس الأيمن داخل عرض VulFi، يمكنك أيضًا إزالة العنصر من النتائج أو إزالة جميع العناصر. يرجى ملاحظة أنه سيتم فقدان أي تعليقات أو تحديثات الحالة بعد إجراء هذه العملية.
اعتبارًا من الإصدار 2.1، يدعم VulFi أيضًا العمليات التي يتم إجراؤها على عناصر متعددة محددة في وقت واحد. يتيح ذلك وضع علامات على عناصر متعددة بحالة معينة، أو حذفها، أو إضافة نفس التعليق إلى صفوف متعددة. يحدث أحيانًا أن العملية لا يتم تحديثها بشكل صحيح. عندما يحدث ذلك، قم بتحديث واجهة المستخدم (Ctrl+U) عدة مرات حتى ترى التغييرات تنعكس.
كلما أردت فحص المثال المكتشف لدالة محتملة الثغرة، فقط انقر نقرًا مزدوجًا في أي مكان في الصف المطلوب وسيأخذك IDA إلى موقع الذاكرة الذي تم تحديده كمحتمل الاهتمام. باستخدام النقر بزر الماوس الأيمن وخيار Set Vulfi Comment يمكنك إدخال تعليق للمثال المحدد (لتبرير الحالة مثلاً).
تسمح الإضافة أيضًا بإنشاء قواعد مخصصة. يمكن تعريف هذه القواعد في واجهة IDA (مثالية للدوال المفردة) أو تقديمها كملف قاعدة مخصص (مثالي للقواعد التي تهدف إلى تغطية دوال متعددة).
عندما ترغب في تتبع دالة مخصصة، والتي تم تحديدها أثناء التحليل، انقر بزر الماوس الأيمن في أي مكان داخل جسمها وحدد Add <name> function to VulFi. يمكنك أيضًا تمييز دالة والنقر بزر الماوس الأيمن على اسم الدالة في عرض التفكيك/مفكك الشيفرة الحالي لتجنب الانتقال إلى جسم الدالة.

من الممكن أيضًا تحميل ملف مخصص يحتوي على مجموعة من القواعد المتعددة. لإنشاء ملف قاعدة مخصص بالهيكل أدناه، يمكنك استخدام ملف القالب المرفق هنا.
[ // مصفوفة من القواعد
{
"name": "RULE NAME", // اسم القاعدة
"function_names":[
"function_name_to_look_for" // قائمة بجميع أسماء الدوال التي يجب مطابقتها مع الشروط المحددة في هذه القاعدة
],
"wrappers":true, // ابحث أيضًا عن أغلفة الدوال المذكورة أعلاه (لاحظ أن الدالة المغلفة يجب أن تطابق القاعدة أيضًا)
"mark_if":{
"High":"True", // إذا كان التقييم صحيحًا، ضع علامة بأولوية عالية (انظر القواعد أدناه)
"Medium":"False", // إذا كان التقييم صحيحًا، ضع علامة بأولوية متوسطة (انظر القواعد أدناه)
"Low": "False" // إذا كان التقييم صحيحًا، ضع علامة بأولوية منخفضة (انظر القواعد أدناه)
}
}
]
مثال على قاعدة تبحث عن جميع المراجع المتبادلة لدالة malloc وتتحقق مما إذا كان معاملها ليس ثابتًا وما إذا كانت قيمة الإرجاع للدالة مفحوصة موضح أدناه:
{
"name": "Possible Null Pointer Dereference",
"function_names":[
"malloc"
],
"wrappers":false,
"mark_if":{
"High":"not param[0].is_constant() and not function_call.return_value_checked()",
"Medium":"False",
"Low": "False"
}
}
Loop Check - يضع علامة على جميع الحلقات. في هذه الحالة param[0] هو عداد الحلقة (إذا تم اكتشافه) و param[1] هو الشرط الذي يتم فحص العداد ضده لإنهاء الحلقة.Array Access - يضع علامة على جميع الوصول إلى المصفوفات. في هذه الحالة param[0] هو المصفوفة و param[1] هو الفهرس.يمكن استخدام نفس الدوال عند إنشاء القواعد كما هو الحال مع معاملات الدوال العادية (أي شيء يمكن استدعاؤه على param[<index>]).
param[<index>]: يستخدم للوصول إلى معامل استدعاء دالة (يبدأ الفهرس من 0)function_call: يستخدم للوصول إلى حدث استدعاء الدالةparam_count: يحمل عدد المعاملات التي تم تمريرها إلى دالةparam[<index>].is_constant()param[<index>].number_value()param[<index>].string_value()param[<index>].set_to_null_after_call()function_call.return_value_checked(<constant_to_check>)param[<index>].used_in_call_<before|after>(["function1","function2"])function_call.reachable_from("<function_name>")param[<index>].is_sign_compared()param[<index>].used_as_index()param[2].number_value() > 5"%s" in param[1].string_value()not param[1].is_constant()function_call.return_value_checked(param_count)function_call.return_value_checked()all(not p.is_constant() for p in param[2:])any(p.is_constant() for p in param)Truestrlen: not param[1].is_constant() and not param[1].used_in_call_before(["strlen"])Low. هذه طريقة لتجنب فقدان المراجع المتبادلة حيث لم يكن من الممكن الحصول على جميع المعاملات بشكل صحيح (ينطبق هذا بشكل أساسي على وضع التفكيك).Ctrl+U لتحديث العرض يدويًا (عدة مرات).read: function_call.reachable_from("read")