我已经复制了示例 3,来自Smashing the Stack for Fun and Profit on 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
我可以从下面的反汇编程序输出中看到,我想跳过 address 处的指令0x0000000000400595
:返回地址 fromcallq <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 个字节长。所以返回地址是从 buffer1[] 开始的 12 个字节。
在 Aleph 1 的示例中,他/她从 buffer1[] 的开始计算返回地址的偏移量为 12 个字节。由于我使用的是 x86_64,而不是 x86_32,因此我需要重新计算返回地址的偏移量。在x86_64上,是不是buffer1[]还是2个字,也就是16个字节;并且 SFP 和返回地址各为 8 个字节(因为我们使用的是 64 位),因此返回地址位于:buf1 + (8 * 2) + 8
相当于buf1 + 24
?