تضمين رمز وخطر البرامج المقرصنة

حول كيف يمكنك تضمين الكود بدون jmp في قسم الكود والبقاء غير مرئي إذا لم تدرس الكود المفكك تمامًا. من يهتم ، من فضلك ، تحت القط.

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

بادئ ذي بدء ، لقد صنعت أداة فكّ الرموز ورمزي يبدو الآن هكذا ، أي تتم قراءة البايت وتمريرها إلى الوظيفة المطلوبة.

void disasm_intel ( unsigned char *ptr, int size, int byte_order, int show ) { show_asm = show; virt = global_virt_text; unsigned char *start = ptr; start_op = ptr; for ( int index = 0; index < size; index++ ) { if ( show_asm == TRUE ) printf ( "%lx: ", virt ); switch ( *ptr ) { case 0x30: intel_opcode_1_0x30 ( &ptr, &index, byte_order ); break; case 0x31: intel_opcode_1_0x31 ( &ptr, &index, byte_order ); break; case 0x66: intel_opcode_1_0x66 ( &ptr, &index, byte_order ); break; case 0x67: intel_opcode_1_0x67 ( &ptr, &index, byte_order ); break; case 0x83: intel_opcode_1_0x83 ( &ptr, &index, byte_order ); break; case 0x88: intel_opcode_1_0x88 ( &ptr, &index, byte_order ); break; // mov register to register byte case 0x89: intel_opcode_1_0x89 ( &ptr, &index, byte_order ); break; case 0x8a: intel_opcode_1_0x8a ( &ptr, &index, byte_order ); break; case 0x8b: intel_opcode_1_0x8b ( &ptr, &index, byte_order ); break; // mov esp, %x : mov ebp, %x case 0x8d: intel_opcode_1_0x8d ( &ptr, &index, byte_order ); break; // lea case 0xb0: intel_opcode_1_0xb0 ( &ptr, &index ); break; // mov al, %x case 0xb1: intel_opcode_1_0xb1 ( &ptr, &index ); break; // mov cl, %x case 0xb2: intel_opcode_1_0xb2 ( &ptr, &index ); break; // mov dl, %x case 0xb3: intel_opcode_1_0xb3 ( &ptr, &index ); break; // mov bl, %x case 0xb4: intel_opcode_1_0xb4 ( &ptr, &index ); break; // mov ah, %x case 0xb5: intel_opcode_1_0xb5 ( &ptr, &index ); break; // mov ch, %x case 0xb6: intel_opcode_1_0xb6 ( &ptr, &index ); break; // mov dh, %x case 0xb7: intel_opcode_1_0xb7 ( &ptr, &index ); break; // mov bh, %x case 0xb8: intel_opcode_1_0xb8 ( &ptr, &index, byte_order ); break; // mov eax, %x case 0xb9: intel_opcode_1_0xb9 ( &ptr, &index, byte_order ); break; // mov ecx, %x case 0xba: intel_opcode_1_0xba ( &ptr, &index, byte_order ); break; // mov edx, %x case 0xbb: intel_opcode_1_0xbb ( &ptr, &index, byte_order ); break; // mov ebx, %x case 0xbe: intel_opcode_1_0xbe ( &ptr, &index, byte_order ); break; // mov esi, %x case 0xbf: intel_opcode_1_0xbf ( &ptr, &index, byte_order ); break; // mov edi, %x case 0xc3: intel_opcode_1_0xc3 ( ); break; // ret case 0xcd: intel_opcode_1_0xcd ( &ptr, &index ); break; // int 0x%x } ptr++; virt += ptr - start; start = ptr; start_op = ptr; } show_asm = FALSE; } 

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

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

 mov eax, [eax + eax + 0x100] 

والحقيقة هي أنه في مثل هذا العنوان يمكن أن يكون هناك ebp وأشر إلى المكدس ، وليس إلى قسم آخر. قررت أن أقوم بذلك بحيث إذا كان العنوان يشير إلى قسم البيانات ، ففكر في الإزاحة عند إدخال الكود ، إذا كان يشير إلى المجموعة ، بمعنى أنه ليس العنوان في قسم البيانات ، فلا تأخذ الإزاحات في الاعتبار.

وبهذه الطريقة يمكن للمهاجمين الاستفادة. بعد كل شيء ، يمكن إدراج رمز ضار في بداية بعض الوظائف في البرنامج ، على سبيل المثال ، بحيث يتم إنشاء طفل شوكة وتنزيل ملف خاص. في Linux ، يمكن القيام بذلك دون أي مشاكل. في الواقع ، في / usr / include هناك ملف مع جميع وظائف النظام لنظام التشغيل. وهذا يعني أنه يمكنك استخدام جزء الشبكة ، حتى إذا لم يكن البرنامج يحتوي على وظائف الشبكة. لا أعرف كيف في نظام التشغيل windows ، لكنني سأحاول إضافة عمل بتنسيق pe لاحقًا. يمكن أن تتحول إلى أن تفعل الشيء نفسه كما هو الحال في لينكس. حتى الآن لدي نسخة وحدة التحكم. ولكن بعد ذلك أخطط للقيام به على gtk.

شكرا لقضاء وقتك في مقالتي.

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


All Articles