【问题标题】:gdb addresses: 0x565561f5 instead of 0x41414141gdb 地址:0x565561f5 而不是 0x41414141
【发布时间】:2019-06-03 16:34:43
【问题描述】:

我想在 c 程序上尝试缓冲区溢出。我用 gcc 像这样gcc -fno-stack-protector -m32 buggy_program.c 编译它。如果我在 gdb 中运行这个程序并且我溢出缓冲区,它应该说 0x41414141,因为我发送了 A。但它说的是0x565561f5。对不起,我的英语不好。有人可以帮帮我吗?

这是源代码:

#include <stdio.h>

int main(int argc, char **argv)
{
    char buffer[64];

    printf("Type in something: ");
    gets(buffer);
}
Starting program: /root/Downloads/a.out 
Type in something: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGSEGV, Segmentation fault.
0x565561f5 in main ()

我想看看这个:

Starting program: /root/Downloads/a.out 
Type in something: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in main ()

【问题讨论】:

  • 为什么觉得奇怪?代码在哪里?
  • 你在这里展示的只是反汇编。这不会告诉你函数将返回到哪里。
  • 尝试使用set disable-randomization off在gdb中启用ASLR。
  • 关于:gets(buffer); 函数:gets() 已经贬值多年,并从最新版本的 C 中完全删除。你的编译器应该告诉你这一点。建议使用fgets()(与gets()有不同的参数)
  • 所有这些 'A' 的输入导致缓冲区溢出:char buffer[64]; 溢出缓冲区会导致未定义的行为。并且该缓冲区在堆栈上声明,因此堆栈被破坏。所以任何事情都有可能发生。

标签: c gdb


【解决方案1】:

查看进程段错误的地址显示反汇编代码中的相关行:

gdb a.out 设置登录
r 拆机主 x/i $eip
p/x $esp

产生以下输出:

(gdb) 启动程序:.../a.out

程序收到信号SIGSEGV,分段错误。 tmp.c:10 10 处的 main (argc=, argv=) 中的 0x08048482 } (gdb) 函数 main 的汇编代码转储:
0x08048436 : lea 0x4(%esp),%ecx
0x0804843a : 和$0xfffffff0,%esp
0x0804843d : pushl -0x4(%ecx)
0x08048440 :推送 %ebp
0x08048441 : mov %esp,%ebp
0x08048443 : 推送 %ebx
0x08048444 : 推送 %ecx
0x08048445 : 低于 $0x40,%esp
0x08048448 :调用
0x8048370 <__x86.get_pc_thunk.bx>
0x0804844d : 添加$0x1bb3,%ebx
0x08048453 : sub $0xc,%esp
0x08048456 : lea -0x1af0(%ebx),%eax
0x0804845c : 推 %eax
0x0804845d : 呼叫 0x8048300
0x08048462 : 添加 $0x10,%esp
0x08048465 : sub $0xc,%esp
0x08048468 : lea -0x48(%ebp),%eax
0x0804846b : 推 %eax
0x0804846c : 呼叫 0x8048310
0x08048471 : 添加 $0x10,%esp
0x08048474 : 移动 $0x0,%eax
0x08048479 : lea -0x8(%ebp),%esp
0x0804847c : pop %ecx
0x0804847d : 弹出 %ebx
0x0804847e : 弹出 %ebp
0x0804847f : lea -0x4(%ecx),%esp
=> 0x08048482 : ret
汇编程序转储结束。
(gdb) => 0x8048482 : ret
(gdb) $1 = 0x4141413d
(gdb) 退出

失败的语句是main 末尾的ret。当ret 尝试从堆栈顶部加载返回地址时,程序失败。生成的可执行文件在与字边界对齐之前将 esp 的旧值存储在堆栈上。当main 完成时,程序会尝试从堆栈中恢复esp,然后读取返回地址。然而,堆栈的整个顶部都受到了损害,从而呈现堆栈指针垃圾的新值 ($1 = 0x4141413d)。执行ret 时,它会尝试从地址 0x4141413d 读取一个字,该地址未分配并产生段错误。

备注

上述反汇编是使用以下编译器选项从问题中的代码生成的:

-m32 -fno-stack-protector -g -O0

【讨论】:

  • 我必须添加 $1 = 0x4141413d 吗?
  • @bitfriends 没有。这是程序崩溃时 esp-register 的值。问题是程序在编译时试图从 main 结束时的堆栈中恢复 esp-registers 值。然而,堆栈受到缓冲区溢出的影响。最后,这很可能归结为使用较旧的编译器(我目前正在研究,但如果有人更快,请随时将信息添加到答案或作为评论)。
  • 那么编译器有问题?
  • @bitfriends 是的。更具体地说是配置。溢出与对齐功能混淆。
  • @bitfriends 对您当前正在研究的主题的一般建议:有很多关于黑客的书籍提供了自己的环境,已经配置为避免上述问题。
【解决方案2】:

各位,我找到了解决方案: 只需用gcc 3.3.4 编译它 gcc -m32 buggy_program.c

【讨论】:

    【解决方案3】:

    现代操作系统使用地址空间布局随机化 ASLR 来使这些东西不那么容易工作。

    我记得刚开始时的争议。 ASLR 对于 32 位进程来说是一个坏主意,因为它对系统施加的其他限制数量以及可疑的安全优势。另一方面,它在 64 位进程上运行良好,现在几乎每个人都在使用它。

    你不知道代码在哪里。你不知道堆在哪里。你不知道堆栈在哪里。现在写漏洞利用很困难。

    另外,您尝试在 64 位进程上使用 32 位 shellcode 和文档。

    在阅读更新后的问题时:您的代码是使用帧指针编译的(这是默认设置)。这导致 ret 指令本身出错,因为 esp 被丢弃。 ASLR 似乎仍在发挥作用,很可能并不重要。

    【讨论】:

    • 我怎样才能把它变成 32 位进程?
    • @bitfriends:就是这个问题:stackoverflow.com/questions/1272357/…
    • 谢谢!但是当我输入 AAAA 0x41414141 时,我怎么能得到呢?它只说返回地址:0x565561f5。
    • ASLR 没有解释为什么导致段错误的地址不是0x41414141,因为堆栈上的返回地址应该被0x41414141 覆盖。当你可以覆盖 $EIP 时,ASLR 让你很难知道你想跳转到哪里。
    • @Steve:在帖子的 v1 中,您可以清楚地看到 gdb 正在报告试图返回未映射内存的指令的地址。所以无论如何,这篇文章已经从我的答案中删除了。
    猜你喜欢
    • 2015-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-22
    • 1970-01-01
    • 2013-05-21
    • 2021-12-30
    • 2017-06-04
    相关资源
    最近更新 更多