如何执行堆栈上的机器指令

时间:2015-10-20 13:19:24

标签: c assembly buffer buffer-overflow machine-instruction

我之前的堆栈帧中有一个返回值,指向后续堆栈帧中的缓冲区。如何获取放在缓冲区中的机器指令来执行?

这甚至可能吗?根据我对堆栈的理解,这是荒谬的。当然,由于堆栈是最后进先出结构。换句话说,一旦PC到达返回地址,缓冲区就会被弹出。

有什么想法吗?

<test>函数(下方)调用<getbuf>函数(也在下面):

08048c53 <test>:
 8048c53:   55                      push   %ebp
 8048c54:   89 e5                   mov    %esp,%ebp
 8048c56:   83 ec 28                sub    $0x28,%esp
 8048c59:   e8 63 04 00 00          call   80490c1 <uniqueval>
 8048c5e:   89 45 f0                mov    %eax,-0x10(%ebp)
 8048c61:   e8 5f 00 00 00          call   8048cc5 <getbuf>
 8048c66:   89 45 f4                mov    %eax,-0xc(%ebp)
 8048c69:   e8 53 04 00 00          call   80490c1 <uniqueval>
 8048c6e:   8b 55 f0                mov    -0x10(%ebp),%edx
 8048c71:   39 d0                   cmp    %edx,%eax
 8048c73:   74 0e                   je     8048c83 <test+0x30>
 8048c75:   c7 04 24 f0 a3 04 08    movl   $0x804a3f0,(%esp)
 8048c7c:   e8 9f fc ff ff          call   8048920 <puts@plt>
 8048c81:   eb 40                   jmp    8048cc3 <test+0x70>
 8048c83:   8b 55 f4                mov    -0xc(%ebp),%edx
 8048c86:   a1 20 e1 04 08          mov    0x804e120,%eax
 8048c8b:   39 c2                   cmp    %eax,%edx
 8048c8d:   75 21                   jne    8048cb0 <test+0x5d>
 8048c8f:   8b 45 f4                mov    -0xc(%ebp),%eax
 8048c92:   89 44 24 04             mov    %eax,0x4(%esp)
 8048c96:   c7 04 24 19 a4 04 08    movl   $0x804a419,(%esp)
 8048c9d:   e8 ae fb ff ff          call   8048850 <printf@plt>
 8048ca2:   c7 04 24 03 00 00 00    movl   $0x3,(%esp)
 8048ca9:   e8 a0 07 00 00          call   804944e <validate>
 8048cae:   eb 13                   jmp    8048cc3 <test+0x70>
 8048cb0:   8b 45 f4                mov    -0xc(%ebp),%eax
 8048cb3:   89 44 24 04             mov    %eax,0x4(%esp)
 8048cb7:   c7 04 24 36 a4 04 08    movl   $0x804a436,(%esp)
 8048cbe:   e8 8d fb ff ff          call   8048850 <printf@plt>
 8048cc3:   c9                      leave
 8048cc4:   c3                      ret

08048cc5 <getbuf>:
 8048cc5:   55                      push   %ebp
 8048cc6:   89 e5                   mov    %esp,%ebp
 8048cc8:   83 ec 38                sub    $0x38,%esp
 8048ccb:   8d 45 d8                lea    -0x28(%ebp),%eax
 8048cce:   89 04 24                mov    %eax,(%esp)
 8048cd1:   e8 32 01 00 00          call   8048e08 <Gets>
 8048cd6:   b8 01 00 00 00          mov    $0x1,%eax
 8048cdb:   c9                      leave
 8048cdc:   c3                      ret

2 个答案:

答案 0 :(得分:3)

  

这甚至可能吗?

如果Data Execution Prevention正在行动,则不会。

  

缓冲区已经弹出

POP / RET不会改变堆栈上的任何数据。它只会更改堆栈指针SP,而数据保持不变,直到被PUSHCALL替换。

这就是为什么像

这样的错误
int* foo() {
  int i = 123;
  return &i;
}
在错误显示之前,

似乎可以工作一段时间。在堆栈上的位置实际被覆盖之前,数据仍然存在不确定的时间。

答案 1 :(得分:1)

是的,可以在数据执行预防之前进行。

一段代码会填充一个比预期更多的缓冲区,它会漂移到堆栈上。此时,您有两种可用的机制。

1)wikipedia: return into libc

2)wikipedia: stack buffer overflow

返回libc

这里用一组函数调用覆盖堆栈(通常在程序中预先存在)。这允许您构建更多代码,并执行您所需的代码。

堆栈缓冲区溢出

覆盖修改保存的帧指针或堆栈上的返回地址。这些更改为堆栈上函数的预期位置。这有点命中注定,因此通常会添加缓冲区(nop-slide)以提高可靠性。

wikipedia : address space layout randomization从程序外部变得更难,因为你无法预测重要的功能和数据在哪里。

数据执行和地址空间布局随机化有助于缓解这些问题,因为通常不需要这些问题。我强烈建议避免使用这种形式的动态编程,因为它很容易被某些恶意实体利用。