gpt4 book ai didi

c++ - 调用从先前执行中保存的函数指针如何失败?

转载 作者:可可西里 更新时间:2023-11-01 17:56:43 26 4
gpt4 key购买 nike

我很好奇函数指针是否可以存储在一个文件中,并在程序退出并重新启动时的某个 future 时间点使用。例如,我的第一个测试程序看起来是这样的伪代码:

void f(){}

typedef void(*Fptr)();

int main() {
int i;
cin >> i;
if (i == 1) {
std::ofstream out(/**/);
out << &f;
}
else {
std::ifstream in(/**/);
Fptr fp;
in >> fp;
fp();
}
}

这正是我想做的事情的逻辑。我会用输入 1 启动它,让它退出,然后用输入 2 再次运行它。不要认为那是我的真实代码,因为我删除了原始测试,因为...

只有在我不更改可执行文件所在的目录时才有效!

向目录添加一个新文件(大概也删除一个文件)并将可执行文件移动到新的地方都会导致 fp(); 崩溃。新函数地址将是一个不同的值。

所以我做了一个新的测试来计算旧函数指针和当前函数地址之间的差异。将该偏移量应用于旧函数指针并调用它会产生正确的函数调用,无论我对目录做了什么。

我相信这是 UB。然而,就像取消引用空指针会导致段错误一样,UB 非常一致。

除了用垃圾重写数据,并假设函数没有加载到 DLL 中,这种方法成功的可能性有多大?它在哪些方面仍然无法工作?

最佳答案

如其他人所述,此问题是由“地址空间布局随机化”(ASLR) 引起的。这种随机化是针对每个模块(即每个可执行镜像)完成的。这意味着,如果您的所有函数都包含在您的 .exe 中,则可以保证它们始终与模块的基址具有相同的偏移量。如果某些函数在 DLL 中,同样适用,但来自 DLL 模块的基础。相关模块地址保持不变很重要,否则无法定位入口点和 DLL 函数。

在 Windows 环境中:

在 Visual Studio(和 MSVC)中,默认情况下 ASLR 处于打开状态,但您可以在“链接器 > 高级 > 随机基址”选项(命令行中的/DYNAMICBASE:NO)中禁用它。通过禁用此选项,函数将始终位于同一地址。

您还可以在运行时确定偏移量。模块基地址可以通过GetModuleHandle()获取(模块句柄实际上就是基地址)。有了它,您可以使用指针的相对地址。

uintptr_t base_address = (uintptr_t)GetModuleHandle(NULL);

uintptr_t offset = (uintptr_t)&f - base_address;
out << offset;

in >> offset;
fp = (Fptr)(offset + base_address);
fp();

关于c++ - 调用从先前执行中保存的函数指针如何失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33561529/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com