【问题标题】:gdb showing different address than in codegdb 显示的地址与代码中的地址不同
【发布时间】:2015-12-22 15:57:39
【问题描述】:

我正在尝试实施缓冲区溢出攻击,我需要知道我试图溢出的缓冲区的地址。

使用 GDB 显示的地址与我刚刚在代码中显示的地址不同:

具体代码:

#include<stdio.h>

int main() {
   char buffer[20];
   printf("%p\n", buffer); // 0xbffff320

   return 0;
}

但是,如果我这样做,在 gdb 中:

p &buffer

我得到:0xbffff330

为什么会有区别,它会破坏我的缓冲区溢出攻击吗?

我禁用了 ALSR 和堆栈保护。

谢谢。

编辑 1:即使我单步执行 gdb 并遇到打印行,我也会得到 0xbffff320 作为地址

编辑 2:

环境:Ubuntu Linux 9 映像在 Windows 7 上的虚拟机中运行。

gdb 版本:6.8-debian

使用 GCC 编译如:gcc -g -fno-stack-protector filename.c 立即执行:./a.out 打印地址:0xbffff320

然后像这样在调试器中打开:gdb ./a.out 然后输入b main 然后run 然后p &amp;buffer

那么地址就是0xbffff330

编辑 3:

这是重现行为的 gdb 日志:

$ gdb ./a.out

b 主要

运行

p &buffer /* 这里的地址与我运行可执行文件时显示的不同 */

单步执行程序到 printf 语句 /* 这里的地址与 p &buffer 相同,但与程序运行时打印的不同 */

【问题讨论】:

  • 你的意思是,在gdb的同一次运行中,printf和gdb的print输出不同的值?
  • 依赖 未定义的行为 是一个非常糟糕的主意。无论如何,我不认为 SO 应该有助于利用漏洞。
  • @Olaf,这是一所大学的项目。我们正在学习如何堆叠粉碎。
  • @hdl,我们只允许编译禁用堆栈保护。所以我们唯一能提供的标志是-fno-stack-protector。 (除了可选的 -g 用于调试,-o 用于重命名可执行文件等)
  • 好的。如果p &amp;buffer in gdb prints the same address as stepping past the printf statement in gdb,那么这与gdb 只是向环境中添加东西是一致的,正如@ouah 的回答所解释的那样。这里还有另一个不同之处:argv[0]。我添加了一个答案来解决这个问题。

标签: c pointers gcc gdb buffer-overflow


【解决方案1】:

据我了解,问题是为什么main 中的局部变量的地址在程序从 shell 启动时与从 gdb 启动时不同。

这里有一个示例程序来显示差异:

mp@ubuntu:~$ cat s.c
#include<stdio.h>

int main(int argc, char **argv) {
  char buffer[20];
  system("env");
  printf("%s %p\n", argv[0], buffer);

  return 0;
}

我们将在干净的环境中运行它。 (我也禁用了 ASLR)。

mp@ubuntu:~$ env -i sh
$ ./s
PWD=/home/mp
./s 0xbffffe48

 

$ gdb ./s
(gdb) run
Starting program: /home/mp/s 
COLUMNS=80
PWD=/home/mp
LINES=42
/home/mp/s 0xbffffe08

gdb 的print &amp;buffer 命令的输出与程序的地址思想相同,但与程序在shell 中运行时不同。

(gdb) b 6
Breakpoint 1 at 0x804849c: file s.c, line 6.
(gdb) run
Starting program: /home/mp/s 
COLUMNS=80
PWD=/home/mp
LINES=42

Breakpoint 1, main (argc=1, argv=0xbffffed4) at s.c:6
6      printf("%s %p\n", argv[0], buffer);
(gdb) p &buffer
$1 = (char (*)[20]) 0xbffffe08
(gdb) n
/home/mp/s 0xbffffe08
8      return 0;

造成这种差异的原因有两点:

  • gdb 使用绝对路径名调用程序,因此 argv 数组更大。
  • gdb 设置(或在本例中添加)两个环境变量。这是在readline/shell.c:sh_set_lines_and_columns() 中完成的。所以 environ 数组更大。

要从环境中删除这两个变量,您可以使用unset environmentset exec-wrapper 运行env -u ...。这样,程序在 gdb 下的地址与在 shell 中运行时的地址相同(如果我们使用绝对路径名)。

$ `pwd`/s
PWD=/home/mp
/home/mp/s 0xbffffe28

$ gdb `pwd`/s
(gdb) set exec-wrapper env -u LINES -u COLUMNS
(gdb) run
Starting program: /home/mp/s 
PWD=/home/mp
/home/mp/s 0xbffffe28

【讨论】:

  • 啊,我明白了,谢谢。这让我困扰了一段时间。环境变量、行和列是什么?
  • 是的,gdb 使用的readline 包设置了它们。它们是终端窗口的尺寸。我还没有研究它为什么这样做。它可能会在内部使用这些值,因此它可以正确显示一个命令行,该命令行太长以至于需要多个终端的物理行。
  • 很高兴知道。再次感谢
【解决方案2】:

您系统中的数组对象存储在堆栈中。在您的堆栈顶部,除其他外,还有环境。当您使用gdb 运行程序时,gdb 将提供一个不同的环境(env var 及其值),从而解释地址差异。

您可以通过在gdb 中运行show environment 并将输出与您的shell 中的set 命令进行比较来检查差异。

【讨论】:

  • 谢谢,我现在看看。
  • 所以你是说gdb显示的环境不同,gdb显示的值比我刚才通过代码做printf要准确和完整得多?
  • 您可以自己尝试使用您的程序。运行你的程序(没有gdb)查看地址。然后export BLA= 用一个长字符串再次运行你的程序(仍然没有gdb)你会看到地址现在不同了。
  • @ouah 但是当程序在单个 gdb 实例中运行时它们有何不同?
  • 如果你使用gdb,ASLR的状态可能不同,你可以在gdb中使用set disable-randomization off禁用它。
【解决方案3】:

发现这是在旧版本的 GDB 中的预期行为(我的是 6.8-debian),如果您正确构建缓冲区溢出攻击,您可以解决此问题,这不会成为问题。

【讨论】:

  • 您能分享一些参考资料吗?你是怎么知道的? :)
  • 我唯一的参考是我的教授告诉我 gdb 在堆栈底部添加了一些指令,以便它可以正确调试。我的观点是,在执行缓冲区溢出攻击的情况下,这种地址差异应该不会对攻击产生太大影响,您仍然可以设法做到这一点。
【解决方案4】:

目前,我能想到的唯一原因是:

  • 您尝试print &amp;buffer您的程序终止后。解决方法:尝试在mainrunnext上设置断点执行printfprint &amp;buffer
  • 您首先在 gdb 外部运行程序,然后在 gdb 内部运行它,但忘记使用 next 执行 printf 行。
  • 您的 gdb 版本中存在错误
  • 您的 gcc 版本中存在错误(gcc 可能会产生不正确的调试信息:请参阅 12

【讨论】:

  • 是的,我做了:“b main”然后“run”,然后立即做了“p &buffer”
  • @Ikshvaku 那么您是否使用next 执行了printf 指令? (见更新的答案)
  • 是的,我做到了。我的教授也刚刚回复说 GDB 在堆栈底部添加了一些东西以帮助它正确调试。但这仍然不能解释为什么您看不到不同的地址。
  • 实际上可能只是我的 GDB 版本更旧(6.8)。你的 GDB 版本是多少?
  • 这次地址还是不一样。尽管地址存在差异,但我实际上能够解决我的缓冲区溢出问题。它非常轻微,不会对攻击产生太大影响。我相信,正如我的教授所说,只是 GDB 给出了这种行为。因此,我会将问题标记为已回答。
猜你喜欢
  • 1970-01-01
  • 2020-10-31
  • 2020-10-02
  • 2021-04-28
  • 1970-01-01
  • 1970-01-01
  • 2018-10-20
  • 1970-01-01
相关资源
最近更新 更多