【问题标题】:Rewriting EBP stack return value重写 EBP 堆栈返回值
【发布时间】:2026-01-16 06:25:01
【问题描述】:

您好,我正在尝试为我构建的一个简单程序编写溢出漏洞利用程序。 Bellow 是我编写的 C 程序。

#include <unistd.h>
#include <string.h>
#include <stdio.h>

char *string_in = "Did not work";

int test(char *this){
char sum_buf[6];
strncpy(sum_buf,this,24);
return 0;
}

void hello(){
printf("hello man");
string_in = "If this triggered, it means our shell code is working\n";
return;
}

int main(int argc, void **argv){

test("01234567890123456789\x00\x40\x06\x02");
printf("My string is %s",string_in);
return 0;

}

基本上发生的情况是,假设该字符串在一个值为 0x00400602 的覆盖 EBP 中读取,这是我的函数 hello() 的返回地址。我知道这是我的函数 hello 的地址值,因为 objdump -d test_stack.o。从对象转储中,我可以看出 rsp 已提前 20 个字节,如下所示

00000000004005b4 <test>:
  4005b4:   55                      push   %rbp
  4005b5:   48 89 e5                mov    %rsp,%rbp
  4005b8:   48 83 ec 20             sub    $0x20,%rsp
  4005bc:   48 89 7d e8             mov    %rdi,-0x18(%rbp)
  4005c0:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
  4005c7:   00 00 
  4005c9:   48 89 45 f8             mov    %rax,-0x8(%rbp)
  4005cd:   31 c0                   xor    %eax,%eax
  4005cf:   48 8b 4d e8             mov    -0x18(%rbp),%rcx
  4005d3:   48 8d 45 f0             lea    -0x10(%rbp),%rax
  4005d7:   ba 18 00 00 00          mov    $0x18,%edx
  4005dc:   48 89 ce                mov    %rcx,%rsi
  4005df:   48 89 c7                mov    %rax,%rdi
  4005e2:   e8 a9 fe ff ff          callq  400490 <strncpy@plt>
  4005e7:   b8 00 00 00 00          mov    $0x0,%eax
  4005ec:   48 8b 55 f8             mov    -0x8(%rbp),%rdx
  4005f0:   64 48 33 14 25 28 00    xor    %fs:0x28,%rdx
  4005f7:   00 00 
  4005f9:   74 05                   je     400600 <test+0x4c>
  4005fb:   e8 a0 fe ff ff          callq  4004a0 <__stack_chk_fail@plt>
  400600:   c9                      leaveq 
  400601:   c3                      retq   

0000000000400602 <hello>:
  400602:   55                      push   %rbp
  400603:   48 89 e5                mov    %rsp,%rbp
  400606:   b8 6d 07 40 00          mov    $0x40076d,%eax
  40060b:   48 89 c7                mov    %rax,%rdi
  40060e:   b8 00 00 00 00          mov    $0x0,%eax
  400613:   e8 98 fe ff ff          callq  4004b0 <printf@plt>
  400618:   48 c7 05 0d 0a 20 00    movq   $0x400778,0x200a0d(%rip)        #

因为低于 $20,%rsp 我知道我需要写至少 20 个字节......但我不确定我需要写多少才能到达我的 rbp。可能从我的 calq 中,我需要写 8 个或我的字节,因为有 2 个 x 调用。虽然我真的不确定我需要写多少。

我像这样编译我的程序...

gcc -g stack.c -o test_stack.o
execstack -s test_stack.o

由于我使用的是 ubuntu 11,我的内核版本是 3.0.17,所以我知道我的 aslr 默认是开启的。我可能需要关闭它,但我不知道该怎么做。我也在运行 i386:x86_64。我可以告诉我在运行期间我的堆栈实际上是什么样的吗?我怎样才能让它发挥作用?如何找到我需要写多少?

感谢您的帮助

【问题讨论】:

  • 我看到的一个问题是您没有为函数地址使用足够的字节。您需要 32 位:\x00\x40\x06\x01。此外,我认为您可能需要颠倒这些字节的顺序,因为 x86 将 0x00400601 在内存中存储为 0x01、0x06、0x40、0x00。最后,不知道我的建议有没有帮助。这有点取决于 gcc 创建堆栈帧的方式。最好在调试器中单步执行,以便查看发生了什么。
  • 你说得对,我忘了输入最后一个字节。谢谢,我会看看这是否有效。
  • 大声笑,现在我没有收到 * 错误。棒极了!但无论出于何种原因,它都不是 printf()。
  • sub $0x20,%rsp 为堆栈帧分配 32 个字节,而不是 20 个(十六进制表示法)。
  • 我有一段时间没有组装,所以我忽略了 0x 作为十六进制。

标签: assembly linux-kernel stack stack-overflow buffer-overflow


【解决方案1】:

第一个问题是我必须关闭堆栈保护并打开 -fno-stack-protector 标志。一旦我这样做了,覆盖我的缓冲区就变得容易多了。第二个问题是,即使字符串是用小端读取和存储的,操作码和操作数似乎也是用大端来解释的。我不明白为什么会这样。最后,我有兴趣覆盖的不是 EBP 堆栈帧……而是我的函数的返回地址。这意味着我需要有效地覆盖高达 32 个字节,而不是写入 24 个字节。我终于让程序工作了!

【讨论】: