首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >粉碎堆栈example3 ala Aleph One

粉碎堆栈example3 ala Aleph One
EN

Stack Overflow用户
提问于 2015-06-02 14:23:57
回答 1查看 677关注 0票数 18

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

代码语言:javascript
复制
0x0000000000400595 <+35>:   movl   $0x1,-0x4(%rbp)

这就是我认为x = 1指令所在的地方。我写了以下几篇文章:

代码语言:javascript
复制
#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个字节吗?

代码语言:javascript
复制
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,在它之前是返回地址。即传递buffer1[]结尾的4个字节。但是请记住,buffer1[]实际上是2字,所以它有8个字节长。所以返回地址是从buffer1[]开始的12个字节。

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-06-08 20:33:15

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

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

问题1,既然您为main()提供了程序集,我可以确认您需要向返回地址(通常是0x0000000000400595 )中添加7个字节,以跳过x=1并转到0x000000000040059c,后者将x加载到寄存器中供以后使用。0x000000000040059c - 0x0000000000400595 = 7

问题2,只添加5个字节,而不是7个字节,这确实会跳到指令的中间。然而,这个2字节的指令尾(纯属偶然)是另一个有效的指令代码.这就是它不会坠毁的原因。

问题3这同样是非常依赖于编译器和设置的。几乎所有的事情都会发生在那里。由于您没有提供反汇编,所以我只能猜测。猜测如下:bufbuf2被舍入到下一个堆栈单元边界(x64上的8个字节)。buf变成8字节,buf2变成16字节。框架指针没有保存到x64上的堆栈中,所以没有"SFP“。总共有24个字节。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30598828

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档