我已经在Linux x86_64上重现了Smashing the Stack for Fun and Profit的第三个示例。然而,我不明白应该增加多少字节到返回地址以跳过指令。
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个字节或7个字节来替代5个字节时一样。
问题3
在Aleph 1的示例中,他/她计算了从buffer1[]的起始位置开始返回地址的偏移量为12个字节。由于我使用的是x86_64而不是x86_32,因此我需要重新计算返回地址的偏移量。当使用x86_64时,buffer1[]仍然是2个字(word),即16个字节;SFP和返回地址各占8个字节(因为我们使用的是64位),因此返回地址位于:就在堆栈上的buffer1[]之前是SFP,在它之前是返回地址。 这超出了buffer1[]的4个字节。但要记住,buffer1[]实际上是2个字, 因此它有8个字节长。所以返回地址距离buffer1[]的开头12个字节。
buf1 + (8 * 2) + 8
,相当于buf1 + 24
?