مقدمة
يهدف هذا المنشور إلى دراسة بعض التقنيات الهندسية العكسية. يتم تقديم جميع المواد لأغراض إعلامية فقط وليس الغرض منها استخدامها في أي مكسب شخصي.أوصت القراءة بعد
الجزء الأولإذا تم تعليم الجراح كيف يعمل الشخص ويمنحه مشرطًا ، فهذا لا يعني أنه سيستخدم هذه المعرفة على حساب شخص ما ، ولا يحلم المجمّع ذو الدراية بكتابة فيروس فائق.
لذلك في هذه الدروس يجب ألا تبحث عن تلميحات من الشقوق والخارقة.
موضوع البحث
نستمر في دراسة رمز البرنامج المساعد لـ Visual Studio Atomineer Pro Documentation (المشار إليها فيما يلي بـ APD). دعنا نتعرف على الأداة وقدراتها.
لذلك ، لنفترض أن لدينا فئة في C ++.
class ClassForReadFile { public: ClassForReadFile(); };
قم بتكوين APD بحيث تكون التعليقات في نمط Doxygen. نحصل على المؤشر في
الفصل الدراسي واضغط
CTRL + SHIFT + D. نحصل على ما يلي:
class ClassForReadFile { public: ClassForReadFile(); };
وأضاف البرنامج المساعد وصفا لطيفا للفئة. كل شيء رائع! نحن نمضي قدما. لنفترض أن الفصل ينتمي إلى مكتبة ويجب علينا تصديره. إضافة ماكرو وتغيير تعريف الفئة
#ifdef DLL_EXPORTS #define DATA_READER_DLL_EXPORTS __declspec(dllexport) #else #define DATA_READER_DLL_EXPORTS __declspec(dllimport) #endif class DATA_READER_DLL_EXPORTS ClassForReadFile { public: ClassForReadFile(); };
بالنسبة إلى نظام C ++ (نظام التشغيل Windows) ، يكون الموقف قياسيًا. تحقق من البرنامج المساعد لدينا. اضغط
CTRL + SHIFT + D واحصل على ما توقعناه على الإطلاق
class DATA_READER_DLL_EXPORTS ClassForReadFile { };
تم تعريف اسم
تعريف DATA_READER_DLL_EXPORTS على أنه اسم الفئة ، بدلاً من
ClassForReadFile ، وتم إنشاء وصف الفئة من هذا الاسم. هذا هو ، في رمز البرنامج المساعد ، هذا الموقف ، أي تصدير الفئة ، إما أنه لم تتم معالجته أو تتم معالجته بخطأ. هذا هو ما سنحاول تصحيحه.
الخطوة 1
سوف نبحث عن أدلة. أولاً ، نظرًا لأن تصدير الوظائف والفئات من C / C ++ هو موقف قياسي ، ما زلنا نحاول "فرض" المكون الإضافي بشكل صحيح. بدلاً من
تعريف DATA_READER_DLL_EXPORTS
، أدخل تعليمة
__declspec نفسها وقم بإنشاء الوثائق
class __declspec(dllexport) ClassForReadFile { };
وها ، لقد حصلوا على وصف الفصل الصحيح! وبالتالي ، نستنتج أنه يوجد في APD بعض التعليمات البرمجية التي تتحقق من وجود السلسلة "__declspec" في وصف الفئة وتتجاهل الخوارزمية الإضافية الخاصة بها لإنشاء الوثائق.
نقوم بفك ترميز المكتبة باستخدام ildasm.exe القياسي من Microsoft SDKs. ابحث عن السطر "__declspec". تم العثور عليه بطريقتين CmdDocComment :: a و CmdDocComment :: b. فئة واحدة. سنخضعها لمزيد من الدراسة.
الخطوة 2
يجب أن أقول على الفور أن ما نبحث عنه موجود في طريقة CmdDocComment ::
هنا يجتمع
__declspec . وتظهر فقط الخطوط الأكثر إثارة للاهتمام.
السلسلة a (CmdDocComment.GeneratorInfo A_0) انتهينا من هذا على أساس أنه بعد التحقق
قائمة [num3] == "__declspec"
طريقة الحذف تسمى
list.RemoveAt (num3) ؛
التفكير (التفكير بصوت عالٍ):
- يحتوي CmdDocComment :: a أسلوب على متغير محلي يحتوي على صفيف سلاسل
List<string> list = A_0.e;
- يخزن العنصر الأول من هذه المصفوفة بداية وصف الوظيفة ، والهيكل ، والفئة ، وما إلى ذلك ، وهذا هو ، الكلمة الأساسية "الفئة" ، "البنية" ، "الاتحاد"
list[0] == "struct"
- كل عنصر من عناصر الصفيف يحتوي على كلمة منفصلة. في حالتنا ، ستكون {"class" ، "DATA_READER_DLL_EXPORTS" ، "ClassForReadFile"}
- هناك حلقة تدور حول كل عناصر المصفوفة "e" ، وتبحث عن العنصر "__declspec" ، وتزيله وكل شيء موجود بين قوسين
- هناك شرط إضافي للخروج من الدورة. هذا هو موقع الكلمات "أين" أو ":". الكلمة الرسمية "أين" هي أن نكون صادقين وغير مألوفين ، ولكن ":" تُستخدم عند وراثة الفصول
حدد خوارزمية جديدة والغرض من التغييرات:
1. التغييرات لا ينبغي أن تؤثر على بقية الوظائف
2. سنقوم بحذف عناصر مجموعة "القائمة" وفقًا للخوارزمية
- تخطي العنصر الأول ؛
- إذا كان العنصر التالي ليس ":" وليس "أين" وليس نهاية الصفيف ، فاحذفه.
اكتب الدورة المطلوبة
يبقى أن البرنامج.
الخطوة 3
البرنامج بصوت عال. البرمجة في الحالة العامة هي كتابة أكواد المصدر ، تجميع ، ربط. لكن المحرمات حرمتنا من هذه الفرصة. سوف نستخدم
أداة dnSpy الموصى
بها . سنقوم بتغيير رموز HEX لأوامر CIL مباشرة في المكتبة ، والتي ، كما اتضح فيما بعد ، مثيرة للغاية وغنية بالمعلومات! لنبدأ. افتح dnSpy ، قم بتحميل المكتبة.
حدد بعض الوقت وقم بتغيير العرض إلى IL
سأقدم أيضًا قائمة ، رغم أنها ضخمة جدًا
الآن لدينا نافذة في أوامر CIL ، تمثيل HEX ، إزاحة الملف والوصف. كل ذلك في مكان واحد. مريحة للغاية (شكرا
CrazyAlex25 ).
دعنا ننتبه إلى الكتلة التي تشير إلى "__declspec". إزاحة الكتلة 0x0001675A. ستكون هذه بداية تعديلاتنا. بعد ذلك ، ابحث عن طريقة RemoveAt. سيكون من المفيد لنا دون تغيير. البايت الأخير من الكتلة هو 0x000167BF. انتقل إلى محرر HEX
Ctrl + X واكتب 0x00 في هذا النطاق. سنوفر و نتحقق مما أدت إليه التغييرات.
حلقة فارغة while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } list.RemoveAt(num3); num3--; num3++; }
الآن سوف ننفذ المنطق الجديد. أولاً ، أضف الشرط
if (num3 != 0 && num3 < list.Count - 1)
يعرض الجدول الأوامر الجديدة ووصفها.
1119 | ldloc.s | يقوم بتحميل متغير محلي بالفهرس المحدد على مكدس الحساب (نموذج قصير). |
---|
2C61 | brfalse.s | تمرير التحكم إلى العبارة الأخيرة إذا كانت القيمة خاطئة أو مرجعًا خالٍ أو صفر. ملاحظة : إذا كان num3 == 0 ، فانتقل إلى خطوة زيادة حلقة التكرار. القيمة 0x64 هي إزاحة العنوان للتعليمات 0x000167BF (انظر القائمة) |
---|
1119 | ldloc.s | تحميل متغير محلي بالفهرس المحدد على مكدس الحساب (نموذج قصير) |
---|
07 | ldloc.1 | تحميل متغير محلي بالفهرس 1 على مكدس الحساب |
---|
6FF700000A | callvirt | get_Count () - يستدعي أسلوب كائن منضم متأخرًا ويدفع القيمة المرجعة إلى مكدس الحساب |
---|
17 | ldc.i4.1 | يدفع القيمة الصحيحة 1 في مكدس حساب كـ int32 |
---|
59 | فرعية | يطرح قيمة واحدة من أخرى ويدفع النتيجة إلى رصة الحساب. |
---|
2F55 | bge.s | ينقل التحكم إلى التعليمة النهائية (نموذج قصير) إذا كانت القيمة الأولى أكبر من أو تساوي الثانية. ملاحظة : إذا كان num3> list.Count - 1 ، فانتقل إلى خطوة زيادة حلقة التكرار. القيمة 0x55 هي إزاحة العنوان قبل الإرشادات 0x000167BF |
---|
نكتب هذه البايتات ابتداءً من الإزاحة 0x0001675A. حفظ وفك الشفرة مرة أخرى
الشرط الأول while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; }
نضيف الآن سلسلة الاختيار "حيث" و ":". أقتبس رمز HEX التالي دون تعليقات إضافية:
07 11 19 17 58 6F F9 00 00 0A 72 A3 1D 00 70 28 70 00 00 0A 2D 3F 07 11 19 17 58 6F F9 00 00 0A 72 92 5E 00 70 28 70 00 00 0A 2D 29
فك والحصول على ما كنت تخطط
دورة جديدة while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } if (num3 != 0 && num3 < list.Count - 1 && !(list[num3 + 1] == ":") && !(list[num3 + 1] == "where")) { list.RemoveAt(num3); num3--; } num3++; }
مع هذه التغييرات ، سيقوم المكون الإضافي بإنشاء الوثائق التالية من الكود:
class DATA_READER_DLL_EXPORTS ClassForReadFile { };
استنتاج
في هذا الدرس ، تعلمنا كيفية تطبيق معرفتنا لإصلاح الأخطاء. بالطبع ، لا يعكس هذا المثال مجموعة كاملة من الأخطاء ومعالجتها ، لكن هذا ليس "صدع عادي". تم إصلاح الخلل الواضح دون الحاجة إلى شفرة المصدر ودون إعادة إنشاء التطبيق.