Я воспроизвел пример 3 из раздела Разрушение стека для развлечения и получения прибыли в 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 <printf@plt>
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 байтах от начала buffer1[].
В примере Алеф 1 вычисляет смещение адреса возврата как 12 байт от начала буфера1[]. Так как я на x86_64, а не на x86_32, мне нужно пересчитать смещение на обратный адрес. На x86_64 буфер1[] по-прежнему состоит из 2 слов, что составляет 16 байтов; и SFP, и адрес возврата составляют 8 байтов каждый (поскольку мы используем 64-разрядную систему), и, следовательно, адрес возврата находится по адресу: buf1 + (8 * 2) + 8
, что эквивалентно buf1 + 24
?