在C/C++中通过内存地址调用函数

34

如果已知函数的原型和其在内存中的地址,是否可以从另一个进程或一些只知道原型和内存地址的代码中调用该函数?如果可能,如何处理返回类型并将其带回代码中?


那么在同一可执行文件的另一个线程中是可能的,但在不同的可执行文件之间呢?如果它是一个共享库呢? - dtech
@jalf 的想法是创建一种句柄,以操纵其他进程的内部。 - dtech
2
@ddriver - 听起来你想要研究进程间通信,而不是函数指针。除非你想要编写一个调试器,在这种情况下,你将面临许多障碍。 - Carl Norum
3
如果你在谈论从不同的进程中进行调用,那么你确实需要指定平台。在大多数现代操作系统中,一个进程所拥有的内存不能被另一个进程随意读取或写入。如果两个进程想要共享一块内存区域,它们必须明确设置。这通常是好事,因为它可以防止一个进程中的错误指针或其他错误导致其他进程甚至整个操作系统崩溃。 - Caleb
1
@Caleb:你想要解决什么问题?一般来说,进程之间的交互有很多限制,从其他进程调用原始函数非常棘手,难以安全可靠地实现。试着退一步,向我们解释一下最终目标是什么,因为听起来现在你可能正在走错方向。 - jalf
显示剩余2条评论
6个回答

60

在现代操作系统中,每个进程都有自己的地址空间,而且地址只在进程内有效。如果你想在另一个进程中执行代码,你必须要么注入一个共享库,要么将你的程序附加为调试器

一旦你进入了其他程序的地址空间,这段代码可以调用任意地址上的函数

typedef int func(void);
func* f = (func*)0xdeadbeef;
int i = f();

6
感谢 @R.Martinho 提供了更好的typedef函数原型的写法(http://chat.stackoverflow.com/transcript/message/2400130#2400130)。我甚至不知道这个方法是行得通的! - sbi
据我所知,在新的操作系统中,没有办法在其他程序的地址空间中写入内存,这会导致SEGFAULT。但是没有任何阻止程序读取任何内存位置(因此,在OpenSSL中,心脏出血漏洞非常可怕,因为您可以使用它读取内存中的任何内容)。那么,您能解释一下为什么我无法访问其他程序的地址空间并需要共享库或调试器吗? - The Quantum Physicist
2
@TheQuantumPhysicist:每个进程都有自己的虚拟内存空间。你的进程中的0xdeadbeef属于你,我的进程中的属于我。它们位于不同的物理内存地址。 - sbi
只有计算机具备MMU时才能使用@abi。如果没有操作系统,计算机甚至可能没有进程... - Jerry Jeremiah

14

是的 - 你描述的是一个函数指针。这里有一个简单的例子:

int (*func)(void) = (int (*)(void))0x12345678;
int x = func();

它可能在进程之间不起作用-在大多数操作系统中,进程不能访问彼此的内存。


为什么要将void ()(void)强制转换为int()(void)来赋值?这很不合逻辑,不是吗? - Geoffroy

8

当您需要直接调用时:

((void(*)(void))0x1234)();

7

之前的回答都不错,但太长了:

int i = ((int (*)(void))0xdeadbeef)();
//                      ==========     --> addr of the function to call
//        =============                --> type of the function to call
//       =========================     --> ... we get a ptr to that fct
//      =============================  --> ... and we call the function

4
不,他们很出色。然而,你的质量非常差劲。完全没有解释。 - Tony

1
在大多数操作系统中,每个进程都有自己的内存,因此你不能共享内存。
示例代码: a.c:
#include <stdio.h>

int r() {return 2;}
int main() {
    printf("%p\n",r);
    while(1);
}

b.c:

#include <stdio.h>

int main() {
int a,(*b)();
scanf("%p",&b);
a=b();
printf("%d\n",a);
return 0;
}

这会导致分段错误。


在C和C++中,你可以访问任何你想要的内存位置,这有利也有弊。 - dtech
@ddriver:C++ 允许您访问任何虚拟内存位置。然而,许多操作系统为每个进程提供自己的虚拟内存,因此它们不能相互影响。但是,有一些特定于操作系统的调用可以使用,以影响另一个进程的内存。 - Mooing Duck
3
@ddriver - 不行。你只能访问操作系统允许你访问的内存位置。你的程序只知道它运行的虚拟内存环境,这通常与物理现实没有太大关系。 - Carl Norum

0

这是完全可能的,但有一些限制。每个进程都有自己的内存块,其他进程无法干扰。现在,你会注意到,我写道这是通过DLL注入(或代码注入)来实现的,所以它是完全可能的。

我们可以使用typedef关键字来实现这一点。现在,我看到你已经将答案标记为“已回答”,并且似乎你已经顺利进行了,这只是提醒其他可能感兴趣的人。


这取决于您的操作系统 - 如果您有一个的话。有些计算机没有硬件MMU,因此没有内存屏障 - 因此即使有操作系统,进程也可以写入彼此的内存。 - Jerry Jeremiah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接