C 粉碎堆栈示例3 ala Aleph One

C 粉碎堆栈示例3 ala Aleph One,c,security,pointers,stack-smash,C,Security,Pointers,Stack Smash,我从Linux x86_64上复制了示例3。但是,我很难理解应该增加到返回地址的正确字节数是多少,以便跳过指令: 0x0000000000400595 <+35>: movl $0x1,-0x4(%rbp) 并在gdb中进行分解。我已禁用地址随机化,并使用-fno stack protector选项编译了程序 问题1 从下面的反汇编程序输出可以看出,我想跳过地址0x0000000000400595处的指令:来自callq的返回地址和movl指令的地址。因此,如果返回地址是0

我从Linux x86_64上复制了示例3。但是,我很难理解应该增加到返回地址的正确字节数是多少,以便跳过指令:

0x0000000000400595 <+35>:   movl   $0x1,-0x4(%rbp)
并在gdb中进行分解。我已禁用地址随机化,并使用
-fno stack protector
选项编译了程序

问题1 从下面的反汇编程序输出可以看出,我想跳过地址
0x0000000000400595
处的指令:来自
callq
的返回地址和
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 
0x0000000000400572:推送%rbp
0x0000000000400573:mov%rsp,%rbp
0x0000000000400576:子$0x10,%rsp
0x000000000040057a:movl$0x0,-0x4(%rbp)
0x0000000000400581:mov$0x3,%edx
0x0000000000400586:mov$0x2,%esi
0x000000000040058b:mov$0x1,%edi
0x0000000000400590:callq 0x40052d
0x0000000000400595:movl$0x1,-0x4(%rbp)
0x000000000040059c:mov-0x4(%rbp),%eax
0x000000000040059f:mov%eax,%esi
0x00000000004005a1:mov$0x40064a,%edi
0x00000000004005a6:mov$0x0,%eax
0x00000000004005ab:callq 0x400410
0x00000000004005b0:LEVEQ
0x00000000004005b1:retq
问题2 我注意到我可以在返回地址中添加5个字节来代替7个字节,并获得相同的结果。当我这样做时,我不是跳进指令的中间吗
0x0000000000400595:movl$0x1,-0x4(%rbp)
?在这种情况下,为什么这不会使程序崩溃,比如我在返回地址中添加了6个字节,而不是5个字节或7个字节

问题3 堆栈上buffer1[]前面是SFP,前面是返回地址。 也就是说,通过缓冲区1[]的末尾传递4个字节。但请记住,buffer1[]是 实际上是2个单词,所以它有8个字节长。因此,返回地址是12个字节 缓冲区1[]的开始


在Aleph 1的示例中,他/她将返回地址的偏移量计算为从buffer1[]开始的12个字节。由于我使用的是x86_64,而不是x86_32,因此需要重新计算返回地址的偏移量。在x86_64上,buffer1[]是否仍然是2个字,即16个字节;SFP和返回地址各为8个字节(我们使用的是64位),因此返回地址位于:
buf1+(8*2)+8
,这相当于
buf1+24

第一点,也是非常重要的一点,需要注意的是:所有数字和偏移量都非常依赖于编译器。不同的编译器,甚至同一个具有不同设置的编译器,可以生成截然不同的程序集。例如,许多编译器可以(也将)删除
buf2
,因为它没有被使用。它们还可以删除
x=0
,因为它的效果不被使用,以后会被覆盖。它们还可以删除
x=1
,并用常量
1
等替换所有出现的
x

也就是说,您绝对需要为您在特定编译器及其设置上获得的特定程序集编号

问题1 由于您为
main()
提供了程序集,因此我可以确认您需要向返回地址添加7个字节(通常为
0x000000000040595
),以跳过
x=1
,转到
0x00000000004059c
,将
x
加载到寄存器中供以后使用<代码>0x000000000040059c-0x0000000000400595=7

问题2 只添加5个字节而不是7个字节确实会跳转到指令的中间。然而,这个2字节的指令尾碰巧是另一个有效的指令代码。这就是为什么它没有崩溃

问题3 这同样非常依赖于编译器和设置。那里几乎什么事都可能发生。由于您没有提供反汇编,我只能猜测。猜测如下:
buf
buf2
向上舍入到下一个堆栈单元边界(x64上的8个字节)
buf
变为8个字节,
buf2
变为16个字节。帧指针未保存到x64上的堆栈,因此没有“SFP”。总共是24个字节。

与相关的可能有助于理解自那时以来发生的变化
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