سؤال

حصلت على مهمة منزلية تطلب مني استدعاء وظيفة دون الاتصال بها بشكل صريح ، باستخدام تدفق المخزن المؤقت. الرمز هو في الأساس:

#include <stdio.h>
#include <stdlib.h>

void g()
{
    printf("now inside g()!\n");
}


void f()
{   
    printf("now inside f()!\n");
    // can only modify this section
    // cant call g(), maybe use g (pointer to function)
}

int main (int argc, char *argv[])
{
    f();
    return 0;
}

على الرغم من أنني لست متأكدًا من كيفية المتابعة. فكرت في تغيير عنوان الإرجاع الخاص بعداد البرنامج بحيث سيتابع مباشرة إلى عنوان G () ، لكنني لست متأكدًا من كيفية الوصول إليه. على أي حال ، ستكون النصائح رائعة.

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

المحلول

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

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

خطوة بخطوة على الضعف الفائض المخزن المؤقت

نصائح أخرى

هذا يعتمد على المترجم ، لذلك لا يمكن إعطاء إجابة واحدة.

سوف يفعل الرمز التالي ما تريده لـ GCC 4.4.1. تجميع مع تعطيل التحسينات (مهم!)

#include <stdio.h>
#include <stdlib.h>

void g()
{
    printf("now inside g()!\n");
}


void f()
{   
  int i;
  void * buffer[1];
  printf("now inside f()!\n");

  // can only modify this section
  // cant call g(), maybe use g (pointer to function)

  // place the address of g all over the stack:
  for (i=0; i<10; i++)
     buffer[i] = (void*) g;

  // and goodbye..
}

int main (int argc, char *argv[])
{
    f();
    return 0;
}

انتاج:

nils@doofnase:~$ gcc overflow.c
nils@doofnase:~$ ./a.out
now inside f()!
now inside g()!
now inside g()!
now inside g()!
now inside g()!
now inside g()!
now inside g()!
Segmentation fault

بما أن هذا هو الواجب المنزلي ، أود أن صدى اقتراح Codeaddict لفهم كيف يعمل الفائض المخزن المؤقت بالفعل.

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

جرب هذه:

void f()
{   
    void *x[1];
    printf("now inside f()!\n");
    // can only modify this section
    // cant call g(), maybe use g (pointer to function)
    x[-1]=&g;
}

ما سر جديدة هذا:

void f()
{   
    void *x[1];
    printf("now inside f()!\n");
    // can only modify this section
    // cant call g(), maybe use g (pointer to function)
    x[1]=&g;
}

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

وظيفة الخاتمة-مثل التجميع المضمّن يضاف إلى f() لتعديل قيمة عنوان الإرجاع على المكدس f() سوف يعود g().

#include <stdio.h>

void g()
{
    printf("now inside g()!\n");
}

void f()
{   
    printf("now inside f()!\n");
    // can only modify this section
    // cant call g(), maybe use g (pointer to function)

    /* x86 function epilogue-like inline assembly */
    /* Causes f() to return to g() on its way back to main() */
    asm(
        "mov %%ebp,%%esp;"
        "pop %%ebp;"
        "push %0;"
        "ret"
        : /* no output registers */
        : "r" (&g)
        : "%ebp", "%esp"
       );
}

int main (int argc, char *argv[])
{
    f();
    return 0;
}

يمكن أن يؤدي فهم كيفية عمل هذا الرمز إلى فهم أفضل لكيفية إعداد إطار مكدس الوظيفة لهندسة معينة تشكل أساس تقنيات التدفق العازلة.

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