سؤال

في طلبي ، لدي بعض الرميات الفراغية (هذا بسبب الأسباب التاريخية ، تم كتابة التطبيق في الأصل في Pure C). في إحدى الوحدات النمطية الخاصة بي ، أعرف أن رميات الفراغ تشير إلى مثيلات من الفصول التي يمكن أن ترث من فئة قاعدة معروفة ، لكن لا يمكنني أن أكون متأكدًا من ذلك بنسبة 100 ٪. لذلك ، فإن القيام بـ Dynamic_cast على الفراغ قد يعطي مشاكل. ربما ، يشير مؤشر الفراغ حتى إلى الهيكل العادي (لذلك لا يوجد VPTR في البنية).

أرغب في التحقيق في أول 4 بايت من الذاكرة التي يشير إليها الفراغ ، لمعرفة ما إذا كان هذا هو عنوان VTABLE الصحيح. أعلم أن هذا هو النظام الأساسي ، وربما حتى خاص بالنسج ، ولكنه قد يساعدني في تحريك التطبيق إلى الأمام ، والتخلص من جميع رميات الفراغ خلال فترة زمنية محدودة (دعنا نقول 3 سنوات).

هل هناك طريقة للحصول على قائمة بجميع VTABLES في التطبيق ، أو طريقة للتحقق مما إذا كان المؤشر يشير إلى VTABLE صالح ، وما إذا كانت هذه الحالة تشير إلى وراثة VTABLE من فئة قاعدة معروفة؟

هل كانت مفيدة؟

المحلول

أرغب في التحقيق في أول 4 بايت من الذاكرة التي يشير إليها الفراغ ، لمعرفة ما إذا كان هذا هو عنوان VTABLE الصحيح.

يمكنك القيام بذلك ، لكن ليس لديك ضمانات على الإطلاق. لا تعرف حتى ما إذا كان الفراغ* سيشير إلى vtable. آخر مرة نظرت فيها إلى هذا (قبل 5 سنوات) وأعتقد أن بعض البراميل قام بتخزين مؤشر VTABLE قبل العنوان الذي أشار إليه مثيل*.

أعلم أن هذا هو المنصة ، وربما حتى التحويلات البرمجية الخاصة بالنسج ،

قد يكون أيضًا محددًا لتصميم البرمجيات ، اعتمادًا على التحسينات التي تستخدمها وما إلى ذلك.

ولكن يمكن أن يساعدني ذلك في تحريك التطبيق إلى الأمام ، والتخلص من جميع رميات الفراغ خلال فترة زمنية محدودة (دعنا نقول 3 سنوات).

هل هذا هو الخيار الوحيد الذي يمكنك رؤيته لنقل التطبيق إلى الأمام؟ هل فكرت في الآخرين؟

هل هناك طريقة للحصول على قائمة بجميع vTables في التطبيق ،

لا :(

أو طريقة للتحقق مما إذا كان المؤشر يشير إلى vtable صالح ،

لا توجد طريقة قياسية. ما يمكنك فعله هو فتح بعض مؤشرات الفصل في مصحح الأخطاء المفضلة لديك (أو يلقي الذاكرة على البايتات وتسجيلها في ملف) ومقارنتها ومعرفة ما إذا كان من المنطقي. ومع ذلك ، ليس لديك أي ضمانات بأن أي من بياناتك (أو مؤشرات أخرى في التطبيق) لن تبدو متشابهة بما فيه الكفاية (عند إلقاء البثات) لإرباك أي رمز تريد.

وما إذا كانت تلك الحالة تشير إلى وراثة VTable من فئة قاعدة معروفة؟

لا مجددا.

فيما يلي بعض الأسئلة (ربما تكون قد فكرت بها بالفعل). قد تمنحك الإجابات على هذه الخيارات ، أو قد تمنحنا أفكارًا أخرى لاقتراحها:

  • ما حجم قاعدة الكود؟ هل من الممكن إدخال تغييرات عالمية ، أم أن الوظائف لنشرها؟

  • هل تعامل جميع المؤشرات بشكل موحد (أي: هل هناك نقاط مشتركة في رمز المصدر الخاص بك حيث يمكنك توصيل البيانات الوصفية الخاصة بك؟)

  • ما الذي يمكنك تغييره في رمز المصدر الخاص بك؟ (إذا كان لديك إمكانية الوصول إلى روتينات تخصيص الذاكرة الخاصة بك أو يمكنك توصيلها بنفسك على سبيل المثال ، فقد تتمكن من توصيل البيانات الوصفية الخاصة بك).

  • إذا تم تمثيل أنواع البيانات المختلفة إلى باطلة* في أجزاء مختلفة من الكود الخاص بك ، كيف يمكنك أن تقرر لاحقًا ما هو في هذه المؤشرات؟ هل يمكنك استخدام الكود الذي يميز الفراغ* لتحديد ما إذا كانت فئات أم لا؟

  • هل تسمح قاعدة الكود الخاصة بك بإعادة إنشاء منهجيات؟ (إعادة التكرار في التكرارات الصغيرة ، عن طريق توصيل تطبيقات بديلة لأجزاء من الكود الخاص بك ، ثم إزالة التنفيذ الأولي واختبار كل شيء)

تعديل (الحل المقترح):

هل الخطوات التالية:

  • حدد فئة بيانات التعريف (قاعدة)

  • استبدل إجراءات تخصيص الذاكرة الخاصة بك بأجهزة مخصصة تشير فقط إلى الإجراءات القياسية / القديمة (وتأكد من أن الكود الخاص بك لا يزال يعمل مع الإجراءات المخصصة).

  • على كل تخصيص ، تخصيص the requested size + sizeof(Metadata*) (وتأكد من أن الكود الخاص بك لا يزال يعمل).

  • استبدال أول sizeof(Metadata*) بايت من تخصيصك مع تسلسل بايت قياسي يمكنك اختباره بسهولة (أنا جزئي إلى 0xdeadbeef: D). ثم ، العودة [allocated address] + sizeof(Metadata*) إلى التطبيق. عند تحديد التخصيص ، خذ المؤشر المستلم ، وقم بتقليله بواسطة `sizeof (metadata*) ، ثم اتصل بالنظام / الروتين السابق لأداء التخصيص. الآن، لديك مخزن مؤقت إضافي مخصص في الكود الخاص بك ، خاصة للبيانات الوصفية في كل تخصيص.

  • في الحالات التي أنت مهتم بوجود بيانات تعريف للبيانات الوصفية ، قم بإنشاء/الحصول على مؤشر فئة بيانات التعريف ، ثم قم بتعيينه في منطقة 0xdeadbeef. عندما تحتاج إلى التحقق من البيانات الوصفية ، reinterpret_cast<Metadata*>([your void* here]), ، قللها ، ثم تحقق مما إذا كانت قيمة المؤشر هي 0xdeadbeef (بدون بيانات تعريف) أو أي شيء آخر.

لاحظ أن هذا الرمز يجب أن يكون موجودًا فقط لإعادة إنشاء - بالنسبة لرمز الإنتاج ، فهو بطيء ، وعرضة للخطأ ، وأشياء سيئة أخرى لا تريد رمز الإنتاج الخاص بك. أود أن أجعل كل هذا الرمز يعتمد على البعض REFACTORING_SUPPORT_ENABLED الماكرو الذي لن يسمح أبداً فئة البيانات الوصفية برؤية ضوء إصدار الإنتاج (باستثناء بنيات الاختبار ربما).

نصائح أخرى

أود أن أقول أنه غير ممكن بدون مرجع ذي صلة (إعلان الرأس).

إذا كنت ترغب في استبدال هذه المؤشرات الفراغ لتصحيح نوع الواجهة ، فإليك ما أعتقد أنه أتمتة:

  1. انتقل عبر قاعدة الكود الخاصة بك للحصول على قائمة بجميع الفئات التي لها وظائف افتراضية ، يمكنك القيام بذلك بسرعة عن طريق كتابة البرنامج النصي ، مثل Perl

  2. اكتب وظيفة تأخذ مؤشر void* كمدخلات ، وتكرر على تلك الفئات ، حاول أن تتمثل في Dynamic_cast ، وتسجيل المعلومات إذا نجحت ، مثل نوع الواجهة ، خط الرمز

  3. استدعاء هذه الوظيفة في أي مكان تستخدمه مؤشر void* ، ربما يمكنك لفها بماكرو حتى تتمكن

  4. قم بتشغيل أتمتة كاملة (إذا كان لديك) وتحليل الإخراج.

تتمثل الطريقة الأسهل في التحميل الزائد operator new لفئة القاعدة الخاصة بك. وبهذه الطريقة ، إذا كنت تعرف أن مؤشرات الفراغ* هي كائنات كومة ، فيمكنك أيضًا تحديد 100 ٪ مما إذا كانت تشير إلى كائنك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top