【问题标题】:Rewriting ret address when leaving a function离开函数时重写ret地址
【发布时间】:2018-12-03 01:03:24
【问题描述】:

我正在尝试从函数 foo() 重写 ret 地址,以在离开函数 foo() 后跳过第一条指令。我正在关注 Smash stack 的乐趣或利润,以及 Jon Erickson 的 Hacking。这是我想跳过“b = 2;”的简单代码指令,所以在 b 中应该存储值 1。

在 gcc 中编译,设置:-fno-stack-protector -z execstack -g
我也关闭了 ASLR。

#include <stdio.h>
int foo() {
    int c = 0;
    int *ptr;
    ptr = (int*) 0x00007fffffffdf38;
    // Second instruction after leaving from foo(). I also tried to skip all instructions before call printf function.
    *ptr = 0x0000000000400577; 
    return 0;
}

int main()
{
    int b = 1;
    foo();
    b = 2;
    printf("b = %d\n", b);      
    return 0;
}

这个程序的输出是:“b = 2”。

我如何假设 RET 指令存储在哪里:

(gdb) disass main
...
0x000000000040056b <+20>:   call   0x40052d <foo>
0x0000000000400570 <+25>:   mov    DWORD PTR [rbp-0x4],0x2
0x0000000000400577 <+32>:   mov    eax,DWORD PTR [rbp-0x4]
0x000000000040057a <+35>:   mov    esi,eax
...
(gdb) break 4       // set breakpoint 1 in function foo before int *ptr;
(gdb) break 7       // set breakpoint 2 in function foo after rewriting RET for checking
(gdb) run
(gdb) x/8x &c       // "x/8x $esp" doesn't work, it says: "Cannot access memory at address 0xffffffffffffdf30", so I'm looking into memory from &c
0x7fffffffdf2c: 0x00000000  0xffffdf50  0x00007fff  0x00400570
0x7fffffffdf3c: 0x00000000  0xffffe030  0x00007fff  0x00000000
// simple math tell me, it should be:
(gdb) x/8xb 0x7fffffffdf38
0x7fffffffdf38: 0x70    0x05    0x40    0x00    0x00    0x00    0x00    0x00

这就是我找到 RET 地址的方式,它位于 0x7fffffffdf38。到这个地方我把下一条指令的地址 - 0x0000000000400577。 但是,即使 RET 被成功改写,计算机仍然没有跳过指令 b = 2。 我检查了它是否真的替换了 RET 地址:

(gdb) c
(gdb) x/8xb 0x7fffffffdf38
0x7fffffffdf38: 0x77    0x05    0x40    0x00    0x00    0x00    0x00    0x00

所以 RET 地址确实被重写了,但是当程序从函数 foo() 离开时,它会跳转到我想跳过的原始地址 0x0000000000400570...

应该很简单,找到存储ret地址的地方,然后把其他地址放到这个地方。我究竟做错了什么? 感谢您的每一个回答。

为了指定任务,我添加了反汇编函数:

Dump of assembler code for function foo:
0x000000000040052d <+0>:    push   rbp
0x000000000040052e <+1>:    mov    rbp,rsp
0x0000000000400531 <+4>:    mov    DWORD PTR [rbp-0x4],0x0
0x0000000000400538 <+11>:   movabs rax,0x7fffffffdf38
0x0000000000400542 <+21>:   mov    QWORD PTR [rbp-0x10],rax
0x0000000000400546 <+25>:   mov    rax,QWORD PTR [rbp-0x10]
0x000000000040054a <+29>:   mov    DWORD PTR [rax],0x400577
0x0000000000400550 <+35>:   mov    eax,0x0
0x0000000000400555 <+40>:   pop    rbp
0x0000000000400556 <+41>:   ret    
End of assembler dump.
(gdb) disass main
Dump of assembler code for function main:
0x0000000000400557 <+0>:    push   rbp
0x0000000000400558 <+1>:    mov    rbp,rsp
0x000000000040055b <+4>:    sub    rsp,0x10
0x000000000040055f <+8>:    mov    DWORD PTR [rbp-0x4],0x1
0x0000000000400566 <+15>:   mov    eax,0x0
0x000000000040056b <+20>:   call   0x40052d <foo>
0x0000000000400570 <+25>:   mov    DWORD PTR [rbp-0x4],0x2
0x0000000000400577 <+32>:   mov    eax,DWORD PTR [rbp-0x4]
0x000000000040057a <+35>:   mov    esi,eax
0x000000000040057c <+37>:   mov    edi,0x400624
0x0000000000400581 <+42>:   mov    eax,0x0
0x0000000000400586 <+47>:   call   0x400410 <printf@plt>
0x000000000040058b <+52>:   mov    eax,0x0
0x0000000000400590 <+57>:   leave  
0x0000000000400591 <+58>:   ret    

【问题讨论】:

  • 我不是 100% 清楚你在做什么,但是调用指令将返回指令推入堆栈。 RET 指令的位置(您的 0x7fffffffdf38 值)似乎无关紧要。您应该在当前堆栈帧中找到返回地址,该地址由call 推送并修改。
  • 我发现(从 dbg,break 4 - inside foo)在 0x7fffffffdf38 上存储了 0x0000000000400570,这是下一条指令。所以 0x7ffffffffdf38 是地方,返回地址存放在哪里,不是吗?
  • 如果您希望我进一步评论,请发布mainfoo 的整个反汇编。
  • foo 内,您的退货地址应位于[rbp+8]
  • 对我来说似乎是对的,但您的问题措辞不当,因为您试图更改 返回地址 而不是 ret 指令。不过,只要两次运行之间的堆栈指针相同,我就没有看到您所做的任何问题。也许更稳定的程序会使用p = &amp;c + 3;

标签: c buffer-overflow


【解决方案1】:

当我在 gdb 调试器中运行它时,它可以工作。所以代码写得很好,问题解决了。问题出在其他地方...

【讨论】:

  • 请编辑您的问题,而不是提供其他信息作为答案。
猜你喜欢
  • 2014-01-13
  • 2021-07-05
  • 1970-01-01
  • 2017-12-22
  • 1970-01-01
  • 2013-10-30
  • 2013-02-27
  • 2011-01-27
  • 1970-01-01
相关资源
最近更新 更多