سؤال

void function(int a, int b, int c) {
   char buffer1[5];
   char buffer2[10];
   int *ret;

   ret = buffer1 + 12;
   (*ret) += 8;//why is it 8??
}

void main() {
  int x;

  x = 0;
  function(1,2,3);
  x = 1;
  printf("%d\n",x);
}

العرض التوضيحي أعلاه من هنا:

http://insecure.org/stf/smashstack.html

لكنه لا يعمل هنا:

D:\test>gcc -Wall -Wextra hw.cpp && a.exe
hw.cpp: In function `void function(int, int, int)':
hw.cpp:6: warning: unused variable 'buffer2'
hw.cpp: At global scope:
hw.cpp:4: warning: unused parameter 'a'
hw.cpp:4: warning: unused parameter 'b'
hw.cpp:4: warning: unused parameter 'c'
1

وأنا لا أفهم لماذا هو الثامن على الرغم من أن المؤلف يعتقد:

القليل من الرياضيات تخبرنا أن المسافة هي 8 بايت.

تفريغ GDB الخاص بي كما يسمى:

Dump of assembler code for function main:
0x004012ee <main+0>:    push   %ebp
0x004012ef <main+1>:    mov    %esp,%ebp
0x004012f1 <main+3>:    sub    $0x18,%esp
0x004012f4 <main+6>:    and    $0xfffffff0,%esp
0x004012f7 <main+9>:    mov    $0x0,%eax
0x004012fc <main+14>:   add    $0xf,%eax
0x004012ff <main+17>:   add    $0xf,%eax
0x00401302 <main+20>:   shr    $0x4,%eax
0x00401305 <main+23>:   shl    $0x4,%eax
0x00401308 <main+26>:   mov    %eax,0xfffffff8(%ebp)
0x0040130b <main+29>:   mov    0xfffffff8(%ebp),%eax
0x0040130e <main+32>:   call   0x401b00 <_alloca>
0x00401313 <main+37>:   call   0x4017b0 <__main>
0x00401318 <main+42>:   movl   $0x0,0xfffffffc(%ebp)
0x0040131f <main+49>:   movl   $0x3,0x8(%esp)
0x00401327 <main+57>:   movl   $0x2,0x4(%esp)
0x0040132f <main+65>:   movl   $0x1,(%esp)
0x00401336 <main+72>:   call   0x4012d0 <function>
0x0040133b <main+77>:   movl   $0x1,0xfffffffc(%ebp)
0x00401342 <main+84>:   mov    0xfffffffc(%ebp),%eax
0x00401345 <main+87>:   mov    %eax,0x4(%esp)
0x00401349 <main+91>:   movl   $0x403000,(%esp)
0x00401350 <main+98>:   call   0x401b60 <printf>
0x00401355 <main+103>:  leave
0x00401356 <main+104>:  ret
0x00401357 <main+105>:  nop
0x00401358 <main+106>:  add    %al,(%eax)
0x0040135a <main+108>:  add    %al,(%eax)
0x0040135c <main+110>:  add    %al,(%eax)
0x0040135e <main+112>:  add    %al,(%eax)
End of assembler dump.

Dump of assembler code for function function:
0x004012d0 <function+0>:        push   %ebp
0x004012d1 <function+1>:        mov    %esp,%ebp
0x004012d3 <function+3>:        sub    $0x38,%esp
0x004012d6 <function+6>:        lea    0xffffffe8(%ebp),%eax
0x004012d9 <function+9>:        add    $0xc,%eax
0x004012dc <function+12>:       mov    %eax,0xffffffd4(%ebp)
0x004012df <function+15>:       mov    0xffffffd4(%ebp),%edx
0x004012e2 <function+18>:       mov    0xffffffd4(%ebp),%eax
0x004012e5 <function+21>:       movzbl (%eax),%eax
0x004012e8 <function+24>:       add    $0x5,%al
0x004012ea <function+26>:       mov    %al,(%edx)
0x004012ec <function+28>:       leave
0x004012ed <function+29>:       ret

في حالتي ، يجب أن تكون المسافة - = 5 ، أليس كذلك؟ ولكن يبدو أنها لا تعمل ..

لماذا function الاحتياجات 56 بايت للمتغيرات المحلية؟ ( sub $0x38,%esp )

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

المحلول

كما أشار جوفها, ، قيمة EIP المحفوظة على المكدس (عنوان الإرجاع) بواسطة call يجب زيادة التعليمات 7 بايت (0x00401342 - 0x0040133b = 7) من أجل تخطي x = 1; تعليمات (movl $0x1,0xfffffffc(%ebp)).

أنت محق في أن 56 بايت محجوز للمتغيرات المحلية (sub $0x38,%esp) ، وبالتالي فإن القطعة المفقودة هي عدد البايتات الماضية buffer1 على المكدس هو EIP المحفوظ.


يخبرني القليل من رمز الاختبار والتجميع المضمّن أن القيمة السحرية هي 28 لاختبار. لا يمكنني تقديم إجابة نهائية عن سبب كونها 28 ، لكنني أفترض أن المترجم يضيف حشوة و/أو كومة الكناري.

تم تجميع الكود التالي باستخدام GCC 3.4.5 (MINGW) واختباره على نظام التشغيل Windows XP SP3 (x86).


unsigned long get_ebp() {
   __asm__("pop %ebp\n\t"
           "movl %ebp,%eax\n\t"
           "push %ebp\n\t");
}

void function(int a, int b, int c) {
   char buffer1[5];
   char buffer2[10];
   int *ret;

   /* distance in bytes from buffer1 to return address on the stack */
   printf("test %d\n", ((get_ebp() + 4) - (unsigned long)&buffer1));

   ret = (int *)(buffer1 + 28);

   (*ret) += 7;
}

void main() {
   int x;

   x = 0;
   function(1,2,3);
   x = 1;
   printf("%d\n",x);
}

يمكن أن يكون لي بنفس القدر من استخدام GDB لتحديد هذه القيمة.

(تجميع ث/ -g لتشمل رموز التصحيح)

(gdb) break function
...
(gdb) run
...
(gdb) p $ebp
$1 = (void *) 0x22ff28
(gdb) p &buffer1
$2 = (char (*)[5]) 0x22ff10
(gdb) quit

(0x22ff28 + 4) - 0x22ff10 = 28

(قيمة EBP + حجم الكلمة) - عنوان Buffer1 = عدد البايتات


بالإضافة إلى تحطيم المكدس من أجل المتعة والربح, أود أن أقترح قراءة بعض المقالات التي ذكرتها إجابتي على سؤال سابق لك و/أو مواد أخرى حول هذا الموضوع. يجب أن يساعدك فهم جيد لكيفية عمل هذا النوع من الاستغلال اكتب رمزًا أكثر أمانًا.

نصائح أخرى

من الصعب التنبؤ بماذا buffer1 + 12 حقا يشير إلى. يمكن أن يضع المترجم الخاص بك buffer1 و buffer2 في أي مكان على المكدس الذي يرغب فيه ، حتى الذهاب إلى حد عدم توفير مساحة ل buffer2 على الاطلاق. الطريقة الوحيدة لمعرفة أين حقًا buffer1 يذهب هو إلقاء نظرة على إخراج المجمع الخاص بالمترجم الخاص بك ، وهناك فرصة جيدة لأنها ستقفز من خلال إعدادات التحسين المختلفة أو الإصدارات المختلفة من نفس المترجم.

لا أختبر الرمز على الجهاز الخاص بي حتى الآن ، لكن هل أخذت محاذاة الذاكرة في الاعتبار؟ حاول تفكيك الرمز باستخدام GCC. أعتقد أن رمز التجميع قد يمنحك فهمًا إضافيًا للرمز. :-)

يطبع هذا الرمز 1 على OpenBSD و FreeBSD ، ويعطي خطأ تجزئة على Linux.

يعتمد هذا النوع من الاستغلال اعتمادًا كبيرًا على كل من مجموعة التعليمات الخاصة بالآلة المعينة ، واتفاقيات الاتصال للمترجم ونظام التشغيل. يتم تعريف كل شيء عن تخطيط المكدس بواسطة التنفيذ ، وليس لغة C. تفترض المقالة Linux على X86 ، ولكن يبدو أنك تستخدم Windows ، وقد يكون نظامك 64 بت ، على الرغم من أنه يمكنك تبديل GCC إلى 32 بت مع -m32.

المعلمات التي يجب عليك تعديلها هي 12 ، وهي الإزاحة من طرف المكدس إلى عنوان الإرجاع ، و 8 ، وهو عدد بايتات main تريد القفز. كما تقول المقالة ، يمكنك استخدام GDB لتفقد تفكيك الوظيفة لمعرفة (أ) إلى أي مدى يتم دفع المكدس عند الاتصال function, و (ب) تعويضات البايت من التعليمات في main.

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

أنت تقوم بتجميع برنامج C مع برنامج التحويل البرمجي C ++. Rename HW.CPP إلى HW.C وستجد أنه سيتم تجميعه.

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