【问题标题】:Is it possible to excecute arbitrary code in C?是否可以在 C 中执行任意代码?
【发布时间】:2018-07-29 21:53:35
【问题描述】:

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

我认为如果将二进制文件导入堆中,将指针移动到寄存器 int,然后尝试预测寄存器并调用指针,这可能是可能的,但我真的没有备用电脑尝试此操作,以防出现问题。

【问题讨论】:

  • 是的,有可能。您需要做的就是调用一个函数,其中指针指向任意代码而不是实际函数的指令。
  • 所以你会做一个模板函数,然后将函数指针等同于另一个位置?
  • 请注意,您执行的任何代码都需要相当“简单”,因为大多数代码都希望加载到特定的内存地址而不是任意定位的数组。您可以将代码编译为与位置无关 (-PIC),但仍然存在涉及修补 GOT 和 PLT 的其他问题。
  • 最简单的方法之一可能是让程序编译所需的代码(通过调用 C 编译器),将其链接到动态库中,然后调用动态库中的例程。这样做的好处是使用现有工具来创建动态链接代码,而不是尝试实现您自己的。当然,它会依赖于实现-系统-版本-一切。
  • 关于“我真的没有备用电脑来尝试这个,以防出现问题”:你不会用软件破坏你的硬件。您不会通过在非特权帐户中运行软件来破坏您的操作系统安装。 (如果您不定期登录系统上的非特权帐户,请创建一个。[当前零售 Microsoft Windows 是否支持该功能?我已经有一段时间没有使用它了。])

标签: c heap-memory metaprogramming


【解决方案1】:

(更新:我可能只见树木不见森林,所以我将首先介绍更明智的动态链接方法。请注意,在此编辑之前已接受答案,因此请参阅下面的原始分隔符。)

如果您想在主程序之外使用代码,操作系统通常会为此提供工具作为其动态链接器的一部分。您通常可以让它在运行时加载.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();
}

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

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

【讨论】:

  • 代码在内存中的地址不一定是它的函数指针的值。例如,Digital Alpha 使用函数描述符来引用函数,它提供了一些参数/寄存器信息以及代码的地址。如果不充分了解 C 语义、他们使用的 C 实现、他们使用的操作系统以及他们使用的硬件,任何人都不应该期望这样的事情能够正常工作。
  • 是否可以更改程序的位置变量以使过程更安全,然后将其更改回来;有点像函数调用,但使用程序位置而不是堆栈基位置?
  • 如果你想让函数为其堆栈使用不同的内存区域,这是可能的,但在 C 中不行。你需要为你想要的特定函数编写一个特定于平台的程序集 shim以这种方式调用。但是,如果您正在寻找,这样做不会提高执行的安全性。为此,您需要 seccomp 之类的东西,或者只是普通的旧仿真。
猜你喜欢
  • 1970-01-01
  • 2010-12-06
  • 2013-05-27
  • 1970-01-01
  • 2010-09-20
  • 2011-08-08
  • 1970-01-01
  • 2011-04-14
  • 2021-12-02
相关资源
最近更新 更多