【问题标题】:Why is a function executed from the same memory address each time?为什么每次都从同一个内存地址执行一个函数?
【发布时间】:2010-03-31 19:29:37
【问题描述】:

我正在反汇编一个可执行文件:

(gdb) disas main
Dump of assembler code for function main:
0x004012d0 <main+0>:    push   %ebp
0x004012d1 <main+1>:    mov    %esp,%ebp
...

每次内存地址都一样:0x004012d0

内存地址不是操作系统动态分配的吗?

更新

现在我看到它是虚拟空间,它可以在某些平台上随机化。

有人可以发布更改的 gdb 转储吗?

【问题讨论】:

  • 建议您相应地标记您的问题
  • 不,没有必要将主题仅限于 windows。
  • 基于有问题的 gdb 和基于 disassebmly 格式我猜这个问题起源于 Linux。看来我错了 - 有人可能也在使用 gdb for Windows。

标签: memory-management x86 disassembly relocation


【解决方案1】:

这取决于操作系统。大多数情况下,二进制文件的地址保持不变。这对于利用内存操作漏洞非常重要,例如缓冲区溢出。 Linux 下链接库的地址总是会因为 ASLR 而不同。在 Windows Vista 和 Windows 7 下,二进制文件的虚拟内存空间在每次执行时也是随机的,因此每次运行的函数地址都会不同。

【讨论】:

  • 看来你错了,我只是尝试在linux中反汇编一个可执行文件,每次都一样。
  • 请注意,The Rook 正在谈论库,因此您可能不会在 main 函数上看到这种效果
  • @Mask,是的,Linux 的 ASLR 不会随机化 TEXT 内存段中的内存。 Windows 7 和 vista 可以。 Linux 的 ASLR 将随机化 HEAP、STACK 和动态链接库。
【解决方案2】:

我认为这里的问题(至少在 Linux 上)可能是 gdb 试图从文档中提供帮助:

设置禁用随机化

设置禁用随机化

此选项(在 gdb 中默认启用)将关闭 启动程序的虚拟地址空间。此选项对于多次调试很有用 会话以使执行更好地重现性和内存地址可重复使用 调试会话。

此功能仅在 gnu/Linux 上实现。您可以使用

获得相同的行为
         (gdb) set exec-wrapper setarch `uname -m` -R

http://sourceware.org/gdb/current/onlinedocs/gdb/Starting.html

更新:我现在已经检查过了,对我来说确实是这样(运行 Linux 2.6.28)。编译一个简单的 Hello World 程序并在没有命令行参数的情况下启动 gdb(我们不想在覆盖 disable-randomization 设置之前加载程序)然后输入:

(gdb) set disable-randomization off
(gdb) file ./a.out
(gdb) break main
(gdb) run
(gdb) disas printf

程序每次运行时printf的地址都不一样。

【讨论】:

  • 为什么不把地址贴在一起?
【解决方案3】:

是和不是。物理内存由操作系统分配,只有操作系统知道您的程序在物理 RAM 中的位置。您的程序只能看到虚拟地址,如果所有内容都以相同的顺序加载,虚拟地址将始终相同。

【讨论】:

    【解决方案4】:

    可执行重定位

    一些可执行文件被设置为总是加载到相同的地址。有些设置为“可重新定位”。在 Visual Studio 链接器中控制此选项的选项称为 /FIXED。即使是这样的可执行文件也最常被加载到首选地址。较新的操作系统(Win7、Vista)随机化了一些可执行文件的加载地址以提高安全性(在未知地址加载的攻击进程更难)——这称为ASLR。注意:即使是标记为 /FIXED:NO 的可执行文件也不假定适用于 ASLR。开发者需要allow ASLR explicitly 获取可执行文件。

    虚拟地址空间

    注意:重要的是要了解进程拥有整个地址空间。多个进程有各自的地址空间,因此如果你多次启动同一个可执行文件,没有理由每次都不能在同一个地址加载它。

    【讨论】:

    • ASLR是修改虚拟地址还是物理地址?
    • 虚拟。物理地址总是会随着时间的推移而改变(即使在一次执行中),无论是否使用 ASLR,因为页面被换入和换出内存。
    • 确切地说:物理地址是对您完全隐藏的东西。我已经为 Windows 游戏编程超过 10 年,甚至经常做一些低级的东西,但我从来不需要关心物理地址。说实话,我什至不知道如何检查某个内存页的物理地址。
    • @Suma,你能发布一个实际改变的 gdb 转储吗?
    • 对不起,我现在的电脑是XP的,不知道怎么在XP上演示。使用 Vista 和标记为“/DYNAMICBASE”的可执行文件应该相当容易。
    【解决方案5】:

    这是一个虚拟地址。物理地址是操作系统已知的,但每个进程都有自己的虚拟地址空间。可重定位映像可能每次都获得相同的映射,尤其是主可执行文件。但不能保证。一个例子是 DLL。 DLL 可能以不同的顺序加载,导致运行之间的虚拟地址不同,因为在加载 DLL 1 时,DLL 2 无法加载到该虚拟地址,必须获取自己的地址。

    【讨论】:

    • 正确。当然,来自不同来源的多个 DLL 可能具有冲突的基地址。
    【解决方案6】:

    这取决于操作系统。在大多数具有虚拟内存的现代操作系统中,可执行代码不需要可重定位,但在较旧的操作系统中,以及在某些专门的操作系统(例如实时、嵌入式)中,代码覆盖可以与位置无关一起使用代码和跳转表。在这种情况下,函数的地址可能会改变,例如如果它的代码段被换出,然后在不同的地址换回。

    【讨论】:

      【解决方案7】:

      这些是来自计算机安全的术语。过去是固定地址(LKML,但直到最近才开始将executeable编译为relocatable,以实现ASLR。(但很久以前,所有库都已编译作为可重定位的,因此库可以在必要时重新加载到不同的地址 - 请阅读 dynamic relocation 但由于加载顺序,那些主要的系统调用 API 通常加载到固定地址。)即使在今天,

      执行 gdb /bin/ls 并按“运行”,你会发现默认地址没有改变:

      (gdb) 反汇编 __open 函数打开的汇编代码转储: 0xb7f017f0 : cmpl $0x0,%gs:0xc 0xb7f017f8 : jne 0xb7f0181c

      无论如何,ASLR 起源于 PaX - 阅读 wiki,它涵盖了很多实现 ASLR 的要求。

      为什么选择 ASLR?防止两种类型的攻击:http://en.wikipedia.org/wiki/Return-to-libc_attackhttp://en.wikipedia.org/wiki/Return-oriented_programming,因为如果在内存中固定,这两种攻击都会假定您的代码区域。

      【讨论】:

        【解决方案8】:

        为什么操作系统会选择不同的地址?

        当操作系统执行一个进程时,它会将一个可执行文件加载到一个虚拟内存空间中。在此过程中,它将解析任何相对和/或符号引用。假设您拥有相同的可执行文件和相同的共享库,并以与上次相同的方式启动它,那么操作系统决定以不同的方式加载可执行文件会很奇怪 方式。

        【讨论】:

        • 顺便说一句,如果您编辑问题以准确指出您正在使用的操作系统,那么有人可能会更好地解释该特定操作系统如何加载和链接程序。
        • 某些操作系统为每次执行选择不同的地址,称为 ASLR。
        • 操作系统可以选择不同的地址,例如,使用 DLL。它是可重定位的,并且取决于它的加载顺序。如果动态加载 DLL,它们可能会在同一程序的执行过程中以不同的顺序加载。
        • 我在您编辑之前阅读了您的回复。它仍然说“为什么操作系统会选择不同的地址?” Rook 和我都提供了原因,因此我投了反对票。
        • 我很抱歉,第二次读起来似乎更好一些,所以我认为它是经过编辑的。我不同意你的反问,我已经列出了原因。我同意你其余的回答,但你的反问暗示操作系统不会选择不同的地址,即使是边缘情况,也会产生误导。
        猜你喜欢
        • 1970-01-01
        • 2021-09-04
        • 2015-07-19
        • 1970-01-01
        • 1970-01-01
        • 2011-05-11
        • 2011-12-01
        • 2021-12-18
        • 1970-01-01
        相关资源
        最近更新 更多