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

ثلاثة متطلبات أساسية لإنشاء "تداخل" فعال
من أجل خداع المفكك ، يجب أن تستوفي الشفرة المتداخلة الشروط الثلاثة التالية: 1) يجب أن تتقاطع التعليمات من سلسلة القناع والسلسلة المخفية مع بعضها البعض ، أي لا يجب محاذاة بعضها البعض (يجب ألا تتزامن وحدات البايت الأولى والأخيرة). خلاف ذلك ، سيكون جزء من الشفرة المخفية مرئيًا في سلسلة الإخفاء. 2) يجب أن تتكون السلسلتان من تعليمات التجميع المعقولة. خلاف ذلك ، سيتم الكشف عن الإخفاء بالفعل في مرحلة التحليل الثابت (بعد أن تعثرت في تعليمات برمجية غير مناسبة للتنفيذ ، سيقوم المفكك بتصحيح مؤشر الأمر وكشف القناع). 3) يجب ألا تكون جميع تعليمات كلا السلسلتين معقولة فحسب ، بل يجب تنفيذها أيضًا بشكل صحيح (لمنع حدوث ذلك ، تعطل البرنامج عند محاولة تنفيذها). خلاف ذلك ، خلال التحليل الديناميكي ، ستجذب الفشل انتباه العكس ، وسيتم الكشف عن القناع.
وصف تقنية تعليمات المجمّع "المتداخلة"
من أجل جعل عملية إنشاء التعليمات البرمجية المتداخلة مرنة قدر الإمكان ، من الضروري تحديد مثل هذه التعليمات متعددة البايت فقط ، والتي يمكن أن يأخذ فيها أكبر عدد ممكن من البايت أي قيمة. ستشكل هذه التعليمات متعددة البايتات سلسلة تعليمات إخفاء.
سعياً وراء هدف إنشاء كود متداخل يستوفي الشروط الثلاثة المذكورة أعلاه ، نعتبر كل تعليمات إخفاء عبارة عن سلسلة من وحدات البايت من النموذج: XX YY ZZ.
هنا XX هي بادئة التعليمات (رمز التعليمات والبايتات الثابتة الأخرى - التي لا يمكن تغييرها).
YY هي وحدات بايت يمكن تغييرها بشكل عشوائي (كقاعدة ، تخزن هذه وحدات البايت القيمة العددية المباشرة التي يتم تمريرها إلى التعليمات ؛ أو عنوان المُعامل المُخزن في الذاكرة). يجب أن يكون هناك أكبر عدد ممكن من وحدات البايت YY بحيث تتناسب معها المزيد من التعليمات المخفية.
ZZ - هذه هي أيضًا وحدات البايت التي يمكن تغييرها بشكل تعسفي ، والفرق الوحيد هو أن مزيج ZZ بايت مع وحدات البايت اللاحقة XX (ZZ XX) يجب أن يشكل تعليمات صالحة تفي بالشروط الثلاثة التي صيغت في بداية المقالة. من الناحية المثالية ، يجب أن تشغل ZZ بايت واحدًا فقط ، لذلك على YY (هذا هو الجزء الأكثر أهمية - يتم وضع الرمز المخفي هنا) يجب أن يكون هناك أكبر عدد ممكن من وحدات البايت. يجب أن ينتهي الأمر الأخير المخفي بـ ZZ ، مما يخلق نقطة تقارب لسلسلتي التنفيذ.
تعليمات اللصق
التركيبة ZZ XX - سنطلق عليها تعليمات اللصق. هناك حاجة إلى تعليمات اللصق ، أولاً ، للانضمام إلى التعليمات المخفية الموجودة في تعليمات التقنيع المجاورة ، وثانيًا ، لاستيفاء الشرط الضروري الأول المذكور في بداية المقالة: يجب أن تتقاطع تعليمات كلا السلسلتين دائمًا مع بعضها البعض (وبالتالي ، فإن تعليمات اللصق دائمًا تقع عند تقاطع تعليمات اخفاء).
يتم تنفيذ تعليمات اللصق في سلسلة أوامر مخفية ، وبالتالي يجب تحديدها بطريقة تفرض أقل عدد ممكن من القيود على الشفرة المخفية. افترض أنه عند تنفيذ ذلك ، يتم تغيير تسجيلات الأغراض العامة وسجل EFLAGS ، فلن يكون الرمز المخفي قادرًا على استخدام السجلات والأوامر الشرطية المقابلة بشكل فعال (على سبيل المثال ، إذا سبقت تعليمات اللصق عامل التشغيل المقارنة ، وتغير تعليمات اللصق نفسها قيمة سجل EFLAGS ، ثم الانتقال الشرطي ، الذي يقف بعد تعليمات اللصق لن يعمل بشكل صحيح).
يتم توضيح الوصف أعلاه لتقنية التداخل في الشكل التالي. إذا بدأ التنفيذ ببايت البدء (XX) ، فسيتم تنشيط سلسلة تعليمات الإخفاء. وإذا كانت من وحدات البايت YY ، يتم تنشيط سلسلة تعليمات مخفية.

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

ومع ذلك ، على الرغم من أن هذه التعليمات تبدو معقولة (من الناحية النظرية ، يمكن تنفيذها بشكل صحيح) ، إلا أنها لا تناسبنا ، لأن معاملها الأول ، كقاعدة ، سيشير إلى عنوان لا يمكن الوصول إليه ، وبالتالي ، عند محاولة تنفيذ مثل هذا MOV ، فإن البرنامج سوف تنهار. T.O. لا يستوفي هذا MOV 10 بايت الشرط الثالث الضروري: يجب تنفيذ جميع تعليمات كلا السلسلتين بشكل صحيح.
لذلك ، سنختار دور تعليمات الإخفاء فقط للمتقدمين الذين لا يشكلون خطر انهيار البرنامج. يضيق هذا الشرط بشكل كبير نطاق التعليمات المناسبة لإنشاء رمز متداخل ، ولكن لا تزال هناك تعليمات مناسبة. فيما يلي أربعة منهم. يحتوي كل من هذه التعليمات الأربعة على خمسة بايت ، والتي يمكن تغييرها بشكل تعسفي ، دون خطر تعطل البرنامج.
- LEA. يحسب هذا التوجيه عنوان الذاكرة المحدد بواسطة التعبير في المُعامل الثاني ويخزن النتيجة في المُعامل الأول. نظرًا لأنه يمكننا الرجوع إلى الذاكرة دون الوصول الفعلي إليها (وبالتالي ، دون خطر تعطل البرنامج) ، يمكن أن تأخذ البايتات الخمس الأخيرة من هذه التعليمات قيمًا عشوائية.

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

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

- لا. يمكن أن تكون NOPs ذات أطوال مختلفة (من 2 إلى 15 بايت) ، اعتمادًا على المعاملات المشار إليها فيها. في هذه الحالة ، لن يكون هناك خطر من تعطل البرنامج (بسبب الوصول إلى عنوان ذاكرة غير صالح). لأن الشيء الوحيد الذي تقوم به NOPs هو زيادة عداد التعليمات ، (لا يقومون بأي عمليات على المعاملات). لذلك ، يمكن أن تأخذ وحدات بايت NOP التي يتم تحديد المعاملات فيها قيمة اعتباطية. ولأغراضنا ، فإن NOP 9 بايت هو الأنسب.

كمرجع ، إليك بعض خيارات NOP الأخرى.

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

عند تشكيل هذه القائمة ، لم نأخذ في الاعتبار سوى تلك الخيارات التي تأخذ ZZ فيها بايت واحد (وإلا فلن يتبقى سوى مساحة صغيرة للشفرة المخفية). فيما يلي قائمة بالتعليمات اللاصقة المناسبة لـ NOP 9 بايت.

من بين قائمة التعليمات هذه ، لا يوجد دليل يخلو من الآثار الجانبية. كل منهم يغير إما EFLAGS ، أو سجلات الأغراض العامة ، أو كليهما مرة واحدة. تنقسم هذه القائمة إلى 4 فئات ، وفقًا للتأثير الجانبي للتعليمات.
تتضمن الفئة الأولى تعليمات لتغيير سجل EFLAGS ، ولكنها لا تغير سجلات الأغراض العامة. يمكن استخدام التعليمات الواردة في هذه الفئة في حالة عدم وجود قفزات شرطية أو أي تعليمات في سلسلة التعليمات المخفية بناءً على معلومات التقييم من سجل EFLAGS. في هذه الحالة ، في هذه الحالة (بالنسبة إلى NOP 9 بايت) هناك أمران فقط: الاختبار و CMP.

فيما يلي مثال بسيط على التعليمات البرمجية المخفية التي تستخدم TEST كإرشادات لصق. يقوم هذا المثال باستدعاء نظام خروج ، والذي يُرجع القيمة 1 لأي إصدار من Linux. من أجل تكوين تعليمات الاختبار لاحتياجاتنا بشكل صحيح ، سنحتاج إلى تعيين البايت الأخير من NOP الأول إلى 0xA9. عندما يقترن هذا البايت بالبايت الأربعة الأولى من NOP التالية (66 0F 1F 84) ، سيتحول إلى تعليمات TAX EAX ، 0x841F0F66. يوضح الشكلان التاليان رمز المجمع المقابل (لسلسلة الإخفاء والسلسلة المخفية). يتم تنشيط السلسلة المخفية عند نقل التحكم إلى البايت الرابع من NOP الأول.


تتضمن الفئة الثانية الإرشادات التي تغير قيم السجلات العامة أو الذاكرة المتاحة (المكدس ، على سبيل المثال) ، ولكن لا تغير سجل EFLAGS. عند تنفيذ تعليمات PUSH أو أي متغير MOV ، حيث يتم تحديد قيمة فورية كمعامل ثانٍ ، يبقى سجل EFLAGS كما هو. T.O. يمكن وضع تعليمات اللصق للفئة الثانية بين تعليمات المقارنة (اختبار ، على سبيل المثال) والتعليمات التي تقيِّم سجل EFLAGS. ومع ذلك ، فإن التعليمات الواردة في هذه الفئة تحد من استخدام السجل الذي يظهر في تعليمات اللصق المقابلة. على سبيل المثال ، إذا تم استخدام MOV EBP ، 0x841F0F66 كإرشادات لصق ، فإن إمكانيات استخدام سجل EBP (من باقي الكود المخفي) محدودة بشكل كبير.
تتضمن الفئة الثالثة تعليمات لتغيير سجل EFLAGS ، وتسجيلات الأغراض العامة (أو الذاكرة) التغيير. لا تحتوي هذه التعليمات على مزايا واضحة على التعليمات من الفئتين الأوليين. ومع ذلك ، يمكن استخدامها أيضًا ، لأنها لا تتعارض مع الشروط الثلاثة التي تم وضعها في بداية المقالة. تتضمن الفئة الرابعة تعليمات ، والتي لا يوجد ضمان على عدم تعطل البرنامج - هناك خطر من الوصول غير القانوني إلى الذاكرة. فمن غير المرغوب فيه للغاية لاستخدامها ، لأن لا يستوفون الشرط الثالث.
تعليمات المُجمع التي يمكن استخدامها في سلسلة مخفية
في حالتنا (عندما يتم استخدام NOPs 9 بايت كتعليمات إخفاء) ، يجب ألا يتجاوز طول كل تعليمة من السلسلة المخفية أربعة بايت (لا ينطبق هذا التقييد على التعليمات اللاصقة التي تشغل 5 بايت). ومع ذلك ، هذا ليس قيدًا خطيرًا للغاية ، لأن معظم التعليمات التي تزيد عن أربعة بايت يمكن أن تتحلل إلى عدة تعليمات أقصر. فيما يلي مثال على MOV 5 بايت أكبر من أن يتناسب مع سلسلة مخفية.

ومع ذلك ، يمكن أن يتحلل هذا MOV الخماسي إلى ثلاث تعليمات ، لا يتجاوز طولها أربعة بايت.

تعزيز القناع عن طريق تشتيت القناع NOPs طوال البرنامج
يبدو عددًا كبيرًا من NOPs المتتالية ، من وجهة نظر العكس ، مريبًا للغاية. من خلال التركيز على اهتمامه بهذه NOPs المشبوهة ، يمكن لمقلوب ذو خبرة الوصول إلى الجزء السفلي من الرمز المخفي فيها. لتجنب هذا التعرض ، يمكن أن تنتشر NOPs المقنعة طوال البرنامج.
يمكن دعم سلسلة التنفيذ الصحيحة للكود المخفي في هذه الحالة من خلال تعليمات مزدوجة البايت للقفز غير المشروط. في هذه الحالة ، سيشغل آخر وحدتي بايت لكل NOP JMP مكون من 2 بايت.
تتيح لك هذه الخدعة تقسيم سلسلة طويلة من NOPs إلى عدة تسلسلات قصيرة (أو حتى استخدام NOP واحد لكل منها). في NOP الأخير لمثل هذا التسلسل القصير ، يمكن تخصيص 3 بايتات فقط من الحمولة (سيتم أخذ البايت الرابع بواسطة تعليمات القفز غير المشروط). T.O. هنا يوجد قيود إضافية على حجم التعليمات الصالحة. ومع ذلك ، كما ذكر أعلاه ، يمكن وضع تعليمات طويلة على سلسلة من التعليمات الأقصر. فيما يلي مثال على نفس MOV 5 بايت ، التي وضعناها بالفعل لتناسب حد 4 بايت. ومع ذلك ، نحن الآن نحلل هذا MOV بطريقة تتناسب مع حد 3 بايت.

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

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