为什么每次运行时函数的地址都会改变?

Why does the address of a function change with every run?

本文关键字:改变 地址 运行时 函数 为什么      更新时间:2023-10-16

我正在努力将地址映射到它们的符号以进行调试(获取调用堆栈)。msdbghelp .dll可以从地址(参见SymFromAddr, MSDN)中区分符号。然而,它不起作用,我想知道这是如何工作的,因为地址似乎随着程序的每次运行而改变:

#include <iostream>
void Foo() {}
int _tmain(int argc, _TCHAR* argv[])
{
    const long unsigned int addr = reinterpret_cast<long unsigned int>(&Foo);
    std::cout << "Address: " << std::hex << addr << std::endl;
    return 0;
}
输出:

D:devSandboxDebug>Sandbox.exe
Address: 901320
D:devSandboxDebug>Sandbox.exe
Address: ce1320
D:devSandboxDebug>Sandbox.exe
Address: 3a1320
D:devSandboxDebug>Sandbox.exe
Address: 3f1320

不同的程序怎么可能从堆栈跟踪中读取地址并将其映射到函数中呢?这听起来像魔法。我在链接的文档中没有发现任何东西说我必须从地址或其他东西中减去什么。

在我的理解中,因为我们克服了实模式,每个进程无论如何都有一个虚拟内存空间,所以不再需要为加载地址掷骰子了。我能理解dll中绝对地址的不确定性,但不能理解主可执行文件。

地址空间布局随机化

因为你的代码被编译为使用地址空间布局随机化,这使得代码不容易受到来自"StackOverflows"的攻击。

如果你真的想改变它,有一个链接器选项。

有一个安全特性可以随机化一些地址,因此潜在的攻击者无法利用已知的固定相对位置。

这是ASLR的作用,正如其他人已经提到的。

看起来您需要做的是在调用SymLoadModuleEx()时为可执行文件指定映像库。是BaseOfDll参数

我不知道它存储在崩溃转储中的确切位置(如果这是其他程序必须使用的东西),但是运行的程序可以使用GetModuleHandle()(这里讨论)获得自己的映像基址。

您可能希望保存加载到进程中的所有dll的名称和基址,而不仅仅是EXE本身的名称和基址。