Я воспроизвел Пример 3 из Smashing the Stack for Fun and Profit в Linux x86_64. Однако у меня возникли проблемы с пониманием того, какое количество байтов должно быть увеличено до адреса возврата, чтобы пропустить команду:
0x0000000000400595 <+35>: movl $0x1,-0x4(%rbp)
где я думаю, что инструкция x = 1
. Я написал следующее:
#include <stdio.h>
void fn(int a, int b, int c) {
char buf1[5];
char buf2[10];
int *ret;
ret = buf1 + 24;
(*ret) += 7;
}
int main() {
int x;
x = 0;
fn(1, 2, 3);
x = 1;
printf("%d\n", x);
}
и разобрал его в gdb. Я отключил рандомизацию адреса и скомпилировал программу с опцией -fno-stack-protector
.
Вопрос 1
Я вижу на выходе дизассемблера ниже, что я хочу пропустить команду по адресу 0x0000000000400595
: и обратный адрес из callq <fn>
, и адрес инструкции movl
. Поэтому, если обратный адрес 0x0000000000400595
, а следующая команда 0x000000000040059c
, я должен добавить 7 байтов к обратному адресу?
0x0000000000400572 <+0>: push %rbp
0x0000000000400573 <+1>: mov %rsp,%rbp
0x0000000000400576 <+4>: sub $0x10,%rsp
0x000000000040057a <+8>: movl $0x0,-0x4(%rbp)
0x0000000000400581 <+15>: mov $0x3,%edx
0x0000000000400586 <+20>: mov $0x2,%esi
0x000000000040058b <+25>: mov $0x1,%edi
0x0000000000400590 <+30>: callq 0x40052d <fn>
0x0000000000400595 <+35>: movl $0x1,-0x4(%rbp)
0x000000000040059c <+42>: mov -0x4(%rbp),%eax
0x000000000040059f <+45>: mov %eax,%esi
0x00000000004005a1 <+47>: mov $0x40064a,%edi
0x00000000004005a6 <+52>: mov $0x0,%eax
0x00000000004005ab <+57>: callq 0x400410 <[email protected]>
0x00000000004005b0 <+62>: leaveq
0x00000000004005b1 <+63>: retq
Вопрос 2
Я замечаю, что я могу добавить 5 байт к обратному адресу вместо 7 и добиться того же результата. Когда я это сделаю, я не прыгаю в середину инструкции 0x0000000000400595 <+35>: movl $0x1,-0x4(%rbp)
? В этом случае, почему это не приводит к сбою программы, например, когда я добавляю 6 байтов в адрес возврата вместо 5 байтов или 7 байтов.
Вопрос 3
Прежде чем buffer1 [] в стеке будет SFP, а перед ним - обратный адрес. То есть 4 байта передают конец buffer1 []. Но помните, что buffer1 [] действительно 2 слова, поэтому его 8 байтов. Таким образом, обратный адрес составляет 12 байтов начало буфера1 [].
В примере Алеф 1 он вычисляет смещение обратного адреса как 12 байт от начала буфера1 []. Поскольку я нахожусь на x86_64, а не x86_32, мне нужно пересчитать смещение на обратный адрес. Когда на x86_64, это так, что buffer1 [] все еще 2 слова, что составляет 16 байт; и SFP и обратный адрес имеют 8 байтов каждый (поскольку мы находимся на 64-битной основе), поэтому адрес возврата: buf1 + (8 * 2) + 8
, что эквивалентно buf1 + 24
?