التطبيقات الهندسية العكسية بعد التعتيم (الجزء الثاني)

مقدمة


يهدف هذا المنشور إلى دراسة بعض التقنيات الهندسية العكسية. يتم تقديم جميع المواد لأغراض إعلامية فقط وليس الغرض منها استخدامها في أي مكسب شخصي.

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

موضوع البحث


نستمر في دراسة رمز البرنامج المساعد لـ Visual Studio Atomineer Pro Documentation (المشار إليها فيما يلي بـ APD). دعنا نتعرف على الأداة وقدراتها. لذلك ، لنفترض أن لدينا فئة في C ++.

class ClassForReadFile { public: ClassForReadFile(); }; 

قم بتكوين APD بحيث تكون التعليقات في نمط Doxygen. نحصل على المؤشر في الفصل الدراسي واضغط CTRL + SHIFT + D. نحصل على ما يلي:

 /** The class for read file. */ 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 واحصل على ما توقعناه على الإطلاق

 /** A data reader DLL exports. */ class DATA_READER_DLL_EXPORTS ClassForReadFile { }; 

تم تعريف اسم تعريف DATA_READER_DLL_EXPORTS على أنه اسم الفئة ، بدلاً من ClassForReadFile ، وتم إنشاء وصف الفئة من هذا الاسم. هذا هو ، في رمز البرنامج المساعد ، هذا الموقف ، أي تصدير الفئة ، إما أنه لم تتم معالجته أو تتم معالجته بخطأ. هذا هو ما سنحاول تصحيحه.

الخطوة 1


سوف نبحث عن أدلة. أولاً ، نظرًا لأن تصدير الوظائف والفئات من C / C ++ هو موقف قياسي ، ما زلنا نحاول "فرض" المكون الإضافي بشكل صحيح. بدلاً من تعريف DATA_READER_DLL_EXPORTS ، أدخل تعليمة __declspec نفسها وقم بإنشاء الوثائق

 /** The class for read file. */ class __declspec(dllexport) ClassForReadFile { }; 

وها ، لقد حصلوا على وصف الفصل الصحيح! وبالتالي ، نستنتج أنه يوجد في APD بعض التعليمات البرمجية التي تتحقق من وجود السلسلة "__declspec" في وصف الفئة وتتجاهل الخوارزمية الإضافية الخاصة بها لإنشاء الوثائق.

نقوم بفك ترميز المكتبة باستخدام ildasm.exe القياسي من Microsoft SDKs. ابحث عن السطر "__declspec". تم العثور عليه بطريقتين CmdDocComment :: a و CmdDocComment :: b. فئة واحدة. سنخضعها لمزيد من الدراسة.

الخطوة 2


يجب أن أقول على الفور أن ما نبحث عنه موجود في طريقة CmdDocComment ::

هنا يجتمع __declspec . وتظهر فقط الخطوط الأكثر إثارة للاهتمام.

السلسلة a (CmdDocComment.GeneratorInfo A_0)
 List<string> e = A_0.e; //....... List<string> list = A_0.e; int num3 = 0; while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } if (list[num3] == "__declspec") { if (num3 + 1 < list.Count) { num = list[num3 + 1].IndexOf(')'); if (num >= 0) { list[num3 + 1] = list[num3 + 1].Substring(num + 1).Trim(); } } list.RemoveAt(num3); num3--; } num3++; } if (list.Count > 0 && (list[0] == "struct" || list[0] == "union")) { if (list.Count == 1) { //...... 


انتهينا من هذا على أساس أنه بعد التحقق
قائمة [num3] == "__declspec"
طريقة الحذف تسمى
list.RemoveAt (num3) ؛
التفكير (التفكير بصوت عالٍ):

  1. يحتوي CmdDocComment :: a أسلوب على متغير محلي يحتوي على صفيف سلاسل

     List<string> list = A_0.e; 
  2. يخزن العنصر الأول من هذه المصفوفة بداية وصف الوظيفة ، والهيكل ، والفئة ، وما إلى ذلك ، وهذا هو ، الكلمة الأساسية "الفئة" ، "البنية" ، "الاتحاد"
     list[0] == "struct" 
  3. كل عنصر من عناصر الصفيف يحتوي على كلمة منفصلة. في حالتنا ، ستكون {"class" ، "DATA_READER_DLL_EXPORTS" ، "ClassForReadFile"}
  4. هناك حلقة تدور حول كل عناصر المصفوفة "e" ، وتبحث عن العنصر "__declspec" ، وتزيله وكل شيء موجود بين قوسين
  5. هناك شرط إضافي للخروج من الدورة. هذا هو موقع الكلمات "أين" أو ":". الكلمة الرسمية "أين" هي أن نكون صادقين وغير مألوفين ، ولكن ":" تُستخدم عند وراثة الفصول

حدد خوارزمية جديدة والغرض من التغييرات:

1. التغييرات لا ينبغي أن تؤثر على بقية الوظائف

2. سنقوم بحذف عناصر مجموعة "القائمة" وفقًا للخوارزمية
- تخطي العنصر الأول ؛
- إذا كان العنصر التالي ليس ":" وليس "أين" وليس نهاية الصفيف ، فاحذفه.

اكتب الدورة المطلوبة

 //     ,          num2 while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { // ,      .     if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } //  if (list[num3] == "__declspec"),  if (num3 != 0 && num3 < (list.Count - 1) && list[num3 + 1] != ":" && list[num3 + 1] != "where") { e.RemoveAt(index); --index; } num3++; } 

يبقى أن البرنامج.

الخطوة 3


البرنامج بصوت عال. البرمجة في الحالة العامة هي كتابة أكواد المصدر ، تجميع ، ربط. لكن المحرمات حرمتنا من هذه الفرصة. سوف نستخدم أداة dnSpy الموصى بها . سنقوم بتغيير رموز HEX لأوامر CIL مباشرة في المكتبة ، والتي ، كما اتضح فيما بعد ، مثيرة للغاية وغنية بالمعلومات! لنبدأ. افتح dnSpy ، قم بتحميل المكتبة.

العثور على طريقتنا
صورة

حدد بعض الوقت وقم بتغيير العرض إلى IL
دورتنا
صورة

سأقدم أيضًا قائمة ، رغم أنها ضخمة جدًا

دورتنا
 /* 0x00016710 07 */ IL_018C: ldloc.1 //         1. /* 0x00016711 1119 */ IL_018D: ldloc.s V_25 //          ( ). /* 0x00016713 6FF900000A */ IL_018F: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016718 72925E0070 */ IL_0194: ldstr "where" //       ,   ,   . /* 0x0001671D 287000000A */ IL_0199: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x00016722 3AAB000000 */ IL_019E: brtrue IL_024E //    ,   value  true,    null   . /* 0x00016727 07 */ IL_01A3: ldloc.1 //         1. /* 0x00016728 1119 */ IL_01A4: ldloc.s V_25 //          ( ). /* 0x0001672A 6FF900000A */ IL_01A6: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x0001672F 72A31D0070 */ IL_01AB: ldstr ":" //       ,   ,   . /* 0x00016734 287000000A */ IL_01B0: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x00016739 3A94000000 */ IL_01B5: brtrue IL_024E //    ,   value  true,    null   . /* 0x0001673E 07 */ IL_01BA: ldloc.1 //         1. /* 0x0001673F 1119 */ IL_01BB: ldloc.s V_25 //          ( ). /* 0x00016741 6FF900000A */ IL_01BD: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016746 03 */ IL_01C2: ldarg.1 //     1   . /* 0x00016747 7B12010004 */ IL_01C3: ldfld string Atomineer.Utils.CmdDocComment/GeneratorInfo::b //      ,       . /* 0x0001674C 287000000A */ IL_01C8: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x00016751 2C07 */ IL_01CD: brfalse.s IL_01D6 //    ,   value  false,    . /* 0x00016753 09 */ IL_01CF: ldloc.3 //         3. /* 0x00016754 16 */ IL_01D0: ldc.i4.0 //    0     int32. /* 0x00016755 2F03 */ IL_01D1: bge.s IL_01D6 //     ( ),        . /* 0x00016757 1119 */ IL_01D3: ldloc.s V_25 //          ( ). /* 0x00016759 0D */ IL_01D5: stloc.3 //                3. /* 0x0001675A 07 */ IL_01D6: ldloc.1 //         1. /* 0x0001675B 1119 */ IL_01D7: ldloc.s V_25 //          ( ). /* 0x0001675D 6FF900000A */ IL_01D9: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016762 729E5E0070 */ IL_01DE: ldstr "__declspec" //       ,   ,   . /* 0x00016767 287000000A */ IL_01E3: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x0001676C 2C51 */ IL_01E8: brfalse.s IL_023B //    ,   value  false,    . /* 0x0001676E 1119 */ IL_01EA: ldloc.s V_25 //          ( ). /* 0x00016770 17 */ IL_01EC: ldc.i4.1 //    1     int32. /* 0x00016771 58 */ IL_01ED: add //         . /* 0x00016772 07 */ IL_01EE: ldloc.1 //         1. /* 0x00016773 6FF700000A */ IL_01EF: callvirt instance int32 class [mscorlib]System.Collections.Generic.List`1<string>::get_Count() //             . /* 0x00016778 2F37 */ IL_01F4: bge.s IL_022D //     ( ),        . /* 0x0001677A 07 */ IL_01F6: ldloc.1 //         1. /* 0x0001677B 1119 */ IL_01F7: ldloc.s V_25 //          ( ). /* 0x0001677D 17 */ IL_01F9: ldc.i4.1 //    1     int32. /* 0x0001677E 58 */ IL_01FA: add //         . /* 0x0001677F 6FF900000A */ IL_01FB: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016784 1F29 */ IL_0200: ldc.i4.s 41 //      int8     int32 ( ). /* 0x00016786 6FC800000A */ IL_0202: callvirt instance int32 [mscorlib]System.String::IndexOf(char) //             . /* 0x0001678B 0C */ IL_0207: stloc.2 //                2. /* 0x0001678C 08 */ IL_0208: ldloc.2 //         2. /* 0x0001678D 16 */ IL_0209: ldc.i4.0 //    0     int32. /* 0x0001678E 3221 */ IL_020A: blt.s IL_022D //     ( ),      . /* 0x00016790 07 */ IL_020C: ldloc.1 //         1. /* 0x00016791 1119 */ IL_020D: ldloc.s V_25 //          ( ). /* 0x00016793 17 */ IL_020F: ldc.i4.1 //    1     int32. /* 0x00016794 58 */ IL_0210: add //         . /* 0x00016795 07 */ IL_0211: ldloc.1 //         1. /* 0x00016796 1119 */ IL_0212: ldloc.s V_25 //          ( ). /* 0x00016798 17 */ IL_0214: ldc.i4.1 //    1     int32. /* 0x00016799 58 */ IL_0215: add //         . /* 0x0001679A 6FF900000A */ IL_0216: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x0001679F 08 */ IL_021B: ldloc.2 //         2. /* 0x000167A0 17 */ IL_021C: ldc.i4.1 //    1     int32. /* 0x000167A1 58 */ IL_021D: add //         . /* 0x000167A2 6FCB00000A */ IL_021E: callvirt instance string [mscorlib]System.String::Substring(int32) //             . /* 0x000167A7 6F8600000A */ IL_0223: callvirt instance string [mscorlib]System.String::Trim() //             . /* 0x000167AC 6FFF00000A */ IL_0228: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::set_Item(int32, !0) //             . /* 0x000167B1 07 */ IL_022D: ldloc.1 //         1. /* 0x000167B2 1119 */ IL_022E: ldloc.s V_25 //          ( ). /* 0x000167B4 6F6701000A */ IL_0230: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::RemoveAt(int32) //             . /* 0x000167B9 1119 */ IL_0235: ldloc.s V_25 //          ( ). /* 0x000167BB 17 */ IL_0237: ldc.i4.1 //    1     int32. /* 0x000167BC 59 */ IL_0238: sub //           . /* 0x000167BD 1319 */ IL_0239: stloc.s V_25 //                index ( ). /* 0x000167BF 1119 */ IL_023B: ldloc.s V_25 //          ( ). /* 0x000167C1 17 */ IL_023D: ldc.i4.1 //    1     int32. /* 0x000167C2 58 */ IL_023E: add //         . /* 0x000167C3 1319 */ IL_023F: stloc.s V_25 //                index ( ). /* 0x000167C5 1119 */ IL_0241: ldloc.s V_25 //          ( ). /* 0x000167C7 07 */ IL_0243: ldloc.1 //         1. /* 0x000167C8 6FF700000A */ IL_0244: callvirt instance int32 class [mscorlib]System.Collections.Generic.List`1<string>::get_Count() //             . /* 0x000167CD 3F3EFFFFFF */ IL_0249: blt IL_018C //    ,     . 


الآن لدينا نافذة في أوامر 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) 

يعرض الجدول الأوامر الجديدة ووصفها.
1119ldloc.sيقوم بتحميل متغير محلي بالفهرس المحدد على مكدس الحساب (نموذج قصير).
2C61brfalse.sتمرير التحكم إلى العبارة الأخيرة إذا كانت القيمة خاطئة أو مرجعًا خالٍ أو صفر. ملاحظة : إذا كان num3 == 0 ، فانتقل إلى خطوة زيادة حلقة التكرار. القيمة 0x64 هي إزاحة العنوان للتعليمات 0x000167BF (انظر القائمة)
1119ldloc.sتحميل متغير محلي بالفهرس المحدد على مكدس الحساب (نموذج قصير)
07ldloc.1تحميل متغير محلي بالفهرس 1 على مكدس الحساب
6FF700000Acallvirtget_Count () - يستدعي أسلوب كائن منضم متأخرًا ويدفع القيمة المرجعة إلى مكدس الحساب
17ldc.i4.1يدفع القيمة الصحيحة 1 في مكدس حساب كـ int32
59فرعيةيطرح قيمة واحدة من أخرى ويدفع النتيجة إلى رصة الحساب.
2F55bge.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; } //     if (num3 != 0 && num3 < list.Count - 1) { list.RemoveAt(num3); num3--; } 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++; } 


مع هذه التغييرات ، سيقوم المكون الإضافي بإنشاء الوثائق التالية من الكود:

 /** The class for read file. */ class DATA_READER_DLL_EXPORTS ClassForReadFile { }; 

استنتاج


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

Source: https://habr.com/ru/post/ar445126/


All Articles