
مجموعة أدوات IDA لتحليل kernelcaches الخاصة بنظام iOS.
تم تحديث هذا الفرع ليعمل على IDA 9.x / Python 3 / iOS 27.0 beta 1
وحدة ida_kernelcache هي وحدة IDAPython لـ IDA Pro لتسهيل العمل مع نوى iOS (kernelcaches). توفر الوحدة دوالً من أجل:
__PRELINK_INFO الخاص بالنواة إلى قاموس Python.__got والدوال الاستدعائية (stub functions) في مقاطع __stubs.دالة المعالجة الرئيسية مصممة لتُشغَّل قبل أي تحليل يدوي أو هندسة عكسية. مع الإعدادات الافتراضية، يميل IDA إلى تفويت الكثير من المعلومات المفيدة في kernelcache. تساعد هذه السكربتات IDA من خلال الاستفادة من البنية المعروفة لـ kernelcache لنشر المعلومات المفيدة تلقائيًا.
بالإضافة إلى الوظائف الأساسية في الوحدة، يحتوي ida_kernelcache على عدة سكربتات لتسهيل تحليل نواة iOS. على سبيل المثال، يمكنك استخدام السكربتات لتوليد هياكل C المستخدمة بواسطة دالة ما تلقائيًا.
العديد من التقنيات المستخدمة في ida_kernelcache طُوِّرت من أجل memctl واقتُبست منه مباشرةً.
تم اختبار ida_kernelcache مع IDA Pro 6.95 على نوى kernelcache لإصدارات iOS 10.1.1 و11.0 و11.2 و11.3.1 و12.0 beta. حاليًا يتم دعم نوى Arm64 الخاصة بـ iOS 10 والإصدارات الأحدث فقط.
تحتاج أولًا إلى تحميل ملف kernelcache غير مضغوط في IDA. يمكنك العثور على رابط تنزيل IPSW معين من Apple عبر الإنترنت، وهناك عدد من الأدوات العامة (بما في ذلك memctl) القادرة على فك ضغط kernelcache.
في IDA، اختر "File" -> "Script file..." من شريط القائمة، ثم اختر سكربت ida_kernelcache.py في الدليل الرئيسي. سيؤدي ذلك إلى تحميل وحدة ida_kernelcache في مترجم IDAPython تحت الاسمين ida_kernelcache وkc. في موجه IDAPython، اكتب kc.kernelcache_process() واضغط Enter لبدء تحليل kernelcache. تؤدي هذه الدالة جميع التحليلات الرئيسية التي يدعمها ida_kernelcache. ستستغرق الدالة عدة دقائق بينما يقوم IDA بتحديد وتحليل الدوال الجديدة.
سيحاول ida_kernelcache ألا يستبدل أسماء المستخدمين للعناوين. هذا يعني أنه إذا تم تحليل kernelcache يدويًا قبل التهيئة باستخدام kernelcache_process، فقد لا تكون النتائج شاملة بنفس القدر لأن الأسماء المحددة من قبل المستخدم قد تمنع نشر الأسماء تلقائيًا. ومع ذلك، لا يوجد أيضًا ضمان بأن ida_kernelcache لن يُفسد التحليل السابق، لذا إذا قررت تشغيل kernelcache_process على ملف kernelcache قمت بتحليله بالفعل، فقم بعمل نسخة احتياطية أولًا.
من المفترض تحميل ida_kernelcache عبر ida_kernelcache.py؛ الوحدات الفرعية في دليل ida_kernelcache ليست مخصصة للتحميل المباشر. ومع ذلك، يكشف ida_kernelcache عن وظائف العديد من هذه الوحدات الفرعية. إليك ما تقوم به كل منها:
ida_utilities:
هذه الوحدة تغلّف بعض دوال IDA لتوفير واجهة برمجة تطبيقات أسهل في الاستخدام. من المفيد بشكل خاص is_mapped وread_word وread_struct وforce_function وReadWords. تتحقق is_mapped مما إذا كان العنوان معينًا (mapped)، وبشكل اختياري مما إذا كان يحتوي على قيمة معروفة. تقرأ read_word كلمة متغيرة الحجم من عنوان. تقرأ read_struct نوع بنية في قاموس Python أو كائن وصول Python، مما يجعل تحليل هياكل البيانات أسهل بكثير. تحاول force_function عدة حيل لتحويل عنوان إلى بداية دالة في IDA. ReadWords هو مولّد (generator) للتكرار على كلمات البيانات وعناوينها في نطاق معين.
build_struct: تحتوي هذه الوحدة الداخلية على أدوات لملء بنية IDA تلقائيًا استنادًا إلى سلسلة من عمليات الوصول إلى البنية.
class_struct:
توفر هذه الوحدة دوالًا لتوليد هياكل IDA تمثل جداول الدوال الافتراضية C++ والطبقات. تقوم initialize_vtable_structs بفحص جداول الدوال الافتراضية (المرمزة) وإنشاء هياكل IDA لحفظ مؤشرات الدوال الافتراضية. تقوم بإجراء تحليل تدفق بيانات على الدوال الافتراضية لتحديد الوصول إلى حقول كل فئة، ثم تبني هياكل IDA لتمثيل الفئات. يتم أيضًا تحويل التعليمات التي يبدو أنها تشير إلى حقل إلى مراجع إزاحة بنية. راجع docstring الخاص بالوحدة لمزيد من التفاصيل.
سكربت ida_kernelcache_reload.py مطابق لـ ida_kernelcache.py، باستثناء أنه يفرض إعادة تحميل وحدة ida_kernelcache وجميع الوحدات الفرعية. وهو مفيد بشكل أساسي للتطوير.
يحتوي دليل scripts على سكربتات تستخدم ida_kernelcache لإجراء نوع من التحليل. هذه السكربتات محددة جدًا بحيث لا يمكن أن تكون جزءًا من وحدة ida_kernelcache الرئيسية، لكنها مفيدة عند الهندسة العكسية لـ kernelcache. وهي تشمل:
find_virtual_method_overrides.py: سكربت للعثور على الفئات المتحدرة من فئة التي تتجاوز أسلوبًا افتراضيًا يحتوي على السلسلة المحددة. تتم طباعة التجاوزات المطابقة على وحدة التحكم.
populate_struct.py: املأ حقول فئة C++ أو بنية C عن طريق إجراء تحليل تدفق بيانات بدءًا من العنوان الحالي.
process_external_methods.py:
قم بمعالجة مصفوفة IOExternalMethod أو IOExternalMethodDispatch إلى نموذج قياسي للاستخدام بواسطة أدوات التضمين (fuzzing).
إذا كنت تستخدم مفكك Hex-Rays، فإن إحدى الميزات الأكثر إثارة للاهتمام في ida_kernelcache هي إعادة بناء فئات C++ تلقائيًا، والتي ستستخدم معلومات OSMetaClass وتحليل تدفق البيانات لإنشاء هياكل IDA لتمثيل الفئات الموجودة في kernelcache. يمكن لهذه التمثيلات أن تحسن بشكل كبير قابلية قراءة تمثيل الكود الزائف (pseudocode). لمعرفة المزيد، راجع المقال Reconstructing C++ classes in the iOS kernelcache using IDA Pro.
مع iOS 12، قدمت Apple تنسيق kernelcache جديدًا على بعض الأجهزة. من بين التغييرات، فإن مؤشرات نواة kernelcache الجديد موسومة لربطها في قائمة، ومن المحتمل أن يكون ذلك للسماح لـ iBoot بتحريك (slide) النواة دون بيانات _PrelinkLinkKASLROffsets في قاموس prelink. محاولة تحليل kernelcache قياسي باستخدام هذا التنسيق في IDA صعبة بسبب غياب المراجع المتبادلة. راجع المقال Analyzing the iOS 12 kernelcache's tagged pointers للحصول على التفاصيل.
إذا كنت تريد فقط إزالة وسم المؤشرات في kernelcache دون إجراء أي معالجة إضافية، فقم بتشغيل kc.tagged_pointers.untag_pointers().
من المحتمل أن بعض هذه الوظائف تنطبق على نطاق أوسع من مجرد نوى Apple (على سبيل المثال، تحليل vtable ونشر الرموز، أو معظم الدوال في ida_utilities.py). ومع ذلك، قمت بتقييد نطاق الاستيراد على وحدة ida_kernelcache فقط لأنني لم أختبر أيًا من هذا على أنواع أخرى من الملفات الثنائية.
تم إصدار ida_kernelcache بموجب رخصة MIT.
الكثير من الوظائف في ida_kernelcache مقتبسة من memctl، والذي تم إصداره أيضًا بموجب رخصة MIT. يُشار إلى المصادر الأخرى في التعليقات في الملفات المقابلة.
Brandon Azad
initialize_class_structsclasses:
تعرف هذه الوحدة نوع ClassInfo الذي يحتفظ بمعلومات حول فئات C++ في kernelcache وتوفر الدالة collect_class_info لفحص kernelcache بحثًا عن الفئات وملء القاموس العام class_info بخريطة من أسماء الفئات إلى كائنات ClassInfo. يسجل نوع ClassInfo اسم الفئة، ومثيل OSMetaClass، وجدول الدوال الافتراضية، واسم الفئة الفائقة (superclass) لكل فئة C++. بالإضافة إلى ذلك، يخزن كل كائن ClassInfo مراجع إلى ClassInfo الخاص بالفئة الفائقة وإلى ClassInfo الخاص بجميع الفئات الفرعية المباشرة، مما يسهل فحص والتنقل عبر التسلسل الهرمي للفئات. كما يخزن collect_class_info مجموعة جميع جداول الدوال الافتراضية في المجموعة العامة vtables.
data_flow: تحتوي هذه الوحدة الداخلية على عمليات تدفق البيانات المستخدمة من قبل باقي أجزاء ida_kernelcache.
kernel:
توفر هذه الوحدة المتغيرين العموميين base وprelink_info. base هو العنوان الأساسي لصورة النواة (بداية ترويسة Mach-O للنواة). prelink_info هو قاموس __PRELINK_INFO المُحلَّل.
kplist:
توفر هذه الوحدة الدالة kplist_parse لتحليل قوائم plist بنمط النواة.
metaclass:
توفر هذه الوحدة الدالة initialize_metaclass_symbols التي تضيف رمزًا لكل مثيل OSMetaClass معروف.
offset:
توفر هذه الوحدة الدالتين initialize_data_offsets وinitialize_offset_symbols. تقوم الأولى بفحص المقاطع بحثًا عن مؤشرات يمكن تحويلها إلى إزاحات. تقوم الأخيرة بترميز الإزاحات في قسم __got لكل kext إذا كان هدف الإزاحة يحتوي على رمز.
segment:
توفر هذه الوحدة الدالة initialize_segments لإعادة تسمية مقاطع IDA لتكون أكثر فائدة. افتراضيًا، يبدو أن IDA ينشئ أسماء المقاطع من خلال الجمع بين تخمين لمعرّف الحزمة (bundle identifier) وقسم Mach-O الذي يصف المنطقة. تستخرج initialize_segments معرّف الحزمة الحقيقي من قاموس __PRELINK_INFO وتعيد تسمية كل مقطع ليشمل معرّف الحزمة ومقطع Mach-O وقسم Mach-O. هذا يجعل من الممكن، على سبيل المثال، التمييز بين __TEXT.__const و__DATA_CONST.__const. توفر هذه الوحدة أيضًا الدالة kernelcache_kext (المعاد تصديرها على المستوى الأعلى) لتحديد الـ kext الذي يحتوي على العنوان المحدد (فقط على تنسيق kernelcache القديم المقسم إلى kexts في iOS 11).
stub:
تحتوي العديد من الـ kexts في kernelcache على دوال استدعائية (stub functions) في قسم __stubs تقفز إلى دوال في النواة نفسها. لسوء الحظ، تشكل هذه الدوال الاستدعائية حاجزًا لنشر المراجع المتبادلة ومعلومات الأنواع. لا تحل هذه الوحدة هذه المشكلات، لكنها تجعل النظر إلى الدوال الاستدعائية أسهل قليلًا عن طريق إعادة تسميتها تلقائيًا بحيث يظهر اسم الدالة الهدف. يتم تحويل الدوال الاستدعائية وأهدافها قسريًا إلى دوال في IDA، مما يساعد في جعل الدوال في IDA متطابقة مع الدوال في الكود المصدري الأصلي.
tagged_pointers: يحتوي تنسيق kernelcache المدمج الجديد في iOS 12 على البايتين العلويين من كل مؤشر موسومين بإزاحة من أجل ربط المؤشرات معًا في قائمة. تحتوي هذه الوحدة على دوال لمعالجة واستعادة تلك المؤشرات الموسومة.
vtable:
توفر هذه الوحدة العديد من الدوال المفيدة للعمل مع جداول الدوال الافتراضية، بما في ذلك vtable_length وconvert_vtable_to_offsets وvtable_overrides وinitialize_vtable_symbols وinitialize_vtable_method_symbols. يتحقق vtable_length مما إذا كان العنوان المحدد يمكن أن يكون جدول دوال افتراضية ويعيد طول الجدول. يقوم المولّد vtable_overrides بتعداد الدوال الافتراضية في فئة والتي تتجاوز دوالًا افتراضية تستخدمها الفئة الفائقة. تضيف الدالة initialize_vtable_symbols رمزًا لبداية كل جدول دوال افتراضية تم تحديده. تقوم initialize_vtable_method_symbols بالتكرار عبر الدوال المتجاوزة في كل جدول وتنشر الرموز من الفئة الفائقة إلى الفئة الفرعية. هذا ممكن لأن معظم الفئات الأساسية في IOKit معرَّفة في XNU مع معلومات رموز كاملة نسبيًا. يجب أن يتوافق كل تجاوز أسلوب في جدول دوال افتراضية لفئة فرعية مع نفس الواجهة التي يتبعها الأسلوب في الفئة الفائقة، مما يعني أنه يمكننا توليد رمز للتجاوز عن طريق استبدال اسم الفئة الفرعية مكان اسم الفئة الفائقة في رمز الأسلوب الافتراضي في الفئة الفائقة. على سبيل المثال، إذا لم يكن لدينا اسم للأسلوب الافتراضي في الفهرس 7 في فئة AppleKeyStore، لكننا نعرف أن الأسلوب الافتراضي في الفهرس 7 في فئته الفائقة IOService يُسمى __ZNK9IOService12getMetaClassEv، فيمكننا استنتاج أن الفهرس 7 يجب أن يُسمى __ZNK13AppleKeyStore12getMetaClassEv في الفئة الفرعية. يمكن استخدام هذه التقنية لترميز معظم الدوال الافتراضية في معظم الفئات.