是否可以在C中执行任意代码?

时间:2018-07-29 21:53:35

标签: c heap metaprogramming

我想知道是否有可能执行根本不在主程序中的代码,如果可能的话,您将如何去执行它?如果不能用c完成,可以用c ++完成吗?

我在想,如果将二进制文件导入到堆上,将指针移到寄存器int,然后尝试预测寄存器并调用指针,则可能会发生这种情况,但是我实际上并没有备用PC尝试此操作,以防出现问题。

1 个答案:

答案 0 :(得分:0)

(更新:我可能错过了树林,因此我将首先介绍更明智的动态链接方法。请注意,此编辑之前已经接受了答案,因此请参阅分隔符下面的内容。)

如果要在主程序之外使用代码,则操作系统通常会在其动态链接器中提供用于此目的的工具。通常,您可以在运行时加载.so.dll文件。

在Linux上,您可以使用dlopen(加载.so文件)和dlsym(从中获取函数)来实现。这是一个虚拟插件系统的示例,其中您向load_plugin传递了一个.so文件的路径,并尝试从该文件运行foo_plugin_init

#include <stdio.h>
#include <dlfcn.h>

int load_plugin(const char* filename) {
    void *handle = dlopen(filename, RTLD_LAZY);  // Try to load the .so file

    dlerror();  // Clear any error a previous dlsym might have reported
    void (*plugin_init)() = dlsym(handle, "foo_plugin_init");  // Load the "foo_plugin_init" function
    const char* error = dlerror();  // Check for errors reported by dlsym
    if (error) {
        fprintf(stderr, "Could not load plugin %s: %s\n", filename, error);
        return 0;
    }

    plugin_init();  // Call the newly loaded function
    return 1;
}

在Windows上,您可以使用LoadLibrary(加载.dll文件)和GetProcAddress(从中获取函数)执行类似的操作。用法大致相同,除了使用GetLastErrorFormatMessage来检索结果字符串而导致的错误报告舞动更为复杂。我的Windows编程非常生锈,因此我无法轻松演示。


只要您有可执行的内存区域,就可以将其强制转换为函数指针:

void execute_memory(const void* mem) {
    void (*func)() = mem;
    func();
}

尽管执行此操作的方法是特定于平台的,但您可能需要首先使该内存可执行。尽管Windows使用mprotect,但大多数操作系统都使用VirtualProtect

请记住,尽管此技术经常在JIT编译器中使用,但是非常很容易意外地编写带有严重安全漏洞的软件,因此,我将尽可能避免使用它。它还违反了可能存在的各种安全策略(例如DEP或grsecurity的W ^ X策略)。因此,如果您打算在实践中使用它,则应该提供一个不依赖于执行运行时生成的代码的后备。