c - 在 mmap 中执行代码以产生可执行代码段错误

标签 c linux assembly function-pointers currying

我正在尝试编写一个函数来复制一个函数(并最终修改其程序集)并返回它。这适用于一级间接寻址,但在两级时我会遇到段错误。

这是一个最小的(不)工作示例:

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

#define BODY_SIZE 100

int f(void) { return 42; }
int (*G(void))(void) { return f; }
int (*(*H(void))(void))(void) { return G; }

int (*g(void))(void) {
    void *r = mmap(0, BODY_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    memcpy(r, f, BODY_SIZE);
    return r;
}

int (*(*h(void))(void))(void) {
    void *r = mmap(0, BODY_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    memcpy(r, g, BODY_SIZE);
    return r;
}

int main() {
    printf("%d\n", f());
    printf("%d\n", G()());
    printf("%d\n", g()());
    printf("%d\n", H()()());
    printf("%d\n", h()()()); // This one fails - why?

    return 0;
}

我可以 memcpy 到一个映射区域一次,以创建一个可以调用的有效函数 (g()())。但是,如果我再次尝试应用它 (h()()()),它就会出现段错误。我已经确认它正确地创建了 g 的复制版本,但是当我执行该版本时,我得到了一个段错误。

有什么原因导致我无法从另一个映射区域执行一个映射区域中的代码?通过使用 x/i 进行的探索性 gdb 检查,我似乎可以成功调用,但是当我返回我来自的函数时,它已被删除并替换为 0。

我怎样才能使这种行为起作用?有可能吗?

大编辑:

许多人询问我的理由,因为我显然在这里做 XY 问题。这是真实的和故意的。你看,不到一个月前this问题已发布在代码 golf stack exchange 上。它也得到了一个很好的 bounty对于 C/Assembly 解决方案。我对这个问题进行了一些思考,并意识到通过复制一个函数体,同时用一些唯一值 stub ,我可以在它的内存中搜索该值并将其替换为有效地址,从而使我能够有效地创建 lambda 函数以单个指针作为参数。使用它我可以获得单一柯里化(Currying)工作,但我需要更通用的柯里化(Currying)。因此,我当前的部分解决方案链接为 here .这是展示我试图避免的段错误的完整代码。虽然这几乎是坏主意的定义,但我觉得它很有趣,并且想知道我的方法是否可行。我唯一缺少的是运行从函数创建的函数的能力,但我无法让它工作。

最佳答案

该代码使用相对调用来调用 mmapmemcpy,因此复制的代码最终调用了无效位置。

您可以通过指针调用它们,例如:

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

#define BODY_SIZE 100

void* (*mmap_ptr)(void *addr, size_t length, int prot, int flags,
                  int fd, off_t offset) = mmap;
void* (*memcpy_ptr)(void *dest, const void *src, size_t n) = memcpy;

int f(void) { return 42; }
int (*G(void))(void) { return f; }
int (*(*H(void))(void))(void) { return G; }

int (*g(void))(void) {
    void *r = mmap_ptr(0, BODY_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    memcpy_ptr(r, f, BODY_SIZE);
    return r;
}

int (*(*h(void))(void))(void) {
    void *r = mmap_ptr(0, BODY_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    memcpy_ptr(r, g, BODY_SIZE);
    return r;
}

int main() {
    printf("%d\n", f());
    printf("%d\n", G()());
    printf("%d\n", g()());
    printf("%d\n", H()()());
    printf("%d\n", h()()()); // This one fails - why?

    return 0;
}

关于c - 在 mmap 中执行代码以产生可执行代码段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50179982/

相关文章:

php - 在 PHP 中处理内存数据

c - receive() 链接消息

linux - bash 变量扩展通过管道传输到 ssh

linux - 基本 assembly 计算器分配不起作用

c - 有没有办法以编程方式生成内联汇编?

c - 翻转C中的位

c++ - 如何配置 Jenkins 以检测 SVN 更改并执行作业?

python - 虚拟环境中的kivy,窗口提供程序错误(linux)

compiler-construction - 如何链接两个nasm源文件

c - 在 C 中的 Struct 中初始化 const Array