mmap 加载共享 object 并获取函数指针

mmap load shared object and get function pointer

我想在不使用 dlfcn.h 中的函数的情况下动态加载库 我有一个充满 .so 文件的文件夹:

gcc -Wall -shared -fPIC -o filename.so filename.c

并且它们都有一个名为:

的入口函数
void __start(int size, char** cmd);

(我知道,可能不是最好的名字)

然后我尝试通过 so 调用 open,然后读取小精灵 header 作为 Elf64_Ehdr 使用 mmap 将其加载到内存中(我知道我应该使用 mprotect 但我想让它先工作,然后添加安全性),最后将 elf header 条目添加到 mmap 返回的指针(加上偏移量 0x100)。

测试代码如下:

#include <elf.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>

typedef void (*ptr_t) (int, char**);

void* alloc_ex_memory(int fd) {
    struct stat s;
    fstat(fd, &s);
    void * ptr = mmap(0, s.st_size, PROT_READ | PROT_WRITE | PROT_EXEC,
    MAP_PRIVATE | MAP_ANONYMOUS, fd, 0);
    if (ptr == (void *)-1) {
        perror("mmap");
        return NULL;
    }
    return ptr;
}

void* load_ex_file(const char* elfFile) {
    Elf64_Ehdr header;
    void * ptr;
    FILE* file = fopen(elfFile, "rb");
    if(file) {
        fread(&header, 1, sizeof(header), file);

        if (memcmp(header.e_ident, ELFMAG, SELFMAG) == 0) {
            ptr = alloc_ex_memory(fileno(file));
            printf("PTR AT -> %p\n", ptr);
            printf("Entry at -> %lx\n", header.e_entry+256);
            ptr = (header.e_entry + 256);
        } else {
            ptr = NULL;
            }

        fclose(file);
            return ptr;
    }
    return NULL;
}

int main(int argc, char** argv) {
    ptr_t func = load_ex_file(argv[1]);
    printf("POINTER AT: %p\n", func);
    getchar();
    func(argc, argv);
    return 0;
}

让我进一步解释一下: 当我 运行 objdump -d filename.so 我得到 _start0x6c0。小精灵 header 入口点 returns 0x5c0 (我补偿它在 dec 中添加 256)。

另外 pmap 显示正在创建的可执行区域可以说 0x7fdf94d0c000 所以我得到和调用的函数指针方向在 0x7fdf94d0c6c0 这应该是入口点并可通过函数指针调用。但我得到的是,你猜对了:段错误。

我想指出的是,我有相同的示例 运行(dlopendlsymdlclose),但它要求我使用 mmap 技巧。我已经看到我的教授成功实施了它,但我不知道如何实施。 (也许我缺少一种更简单的方法)。

我的代码基于 this jit example

提前致谢!

编辑:这是我编译成 .so 的 .c 代码:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void __start(int size, char** cmd) {
    if (size==2) {
        if (!strcmp(cmd[1], "-l")) {
            printf("******.********.*****@udc.es\n");
            return;
        } else if (!strcmp(cmd[1], "-n")) {
            printf("****** ******** *****\n");
            return;
        }
    } else if (size==1) {
            printf("****** ******** ***** (******.********.*****@udc.es)\n");
            return;
    }
    printf("Wrong command syntax: autores [-l | -n]\n");
}

its required that i use the mmap trick. I have seen my professor implement it successfully but i cannot figure out how.

为此,您的 __start 必须完全独立,并且 调用任何其他库(您调用 [=12= 违反了该要求] 和 printf).

当你调用一个未解析的符号时,你要求动态加载器解析那个符号,加载器需要各种信息。该信息通常在 dlopen 通话期间设置。由于您绕过了 dlopen,因此您的程序崩溃 一点也不 令人惊讶。

您的第一步应该是更改您的代码,使 __start 根本不执行任何操作(为空),然后验证您是否可以调用 __start。这将确认您正在正确计算其地址。

其次,为该代码添加崩溃:

void __start(...)
{
  int *p = NULL;
  *p = 42;  // should crash here
}

并确认您现在正在观察崩溃。这将确认您的代码正在被调用。

第三,删除崩溃代码并使用直接系统调用(例如write(1, "Hello\n", 6)(但不要调用write——你必须实现它直接在你的库中))来实现你需要的任何东西。