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

时间:2018-11-14 10:06:46

标签: c elf mmap

我想不使用dlfcn.h中的函数来动态加载库,但我有一个充满.so文件的文件夹,该文件使用以下命令编译:

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

所有这些都有一个名为:

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

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

然后我尝试在此之上调用open,然后读取Elf64_Ehdr的elf标头,并使用mmap将其装入内存(我知道我应该使用mprotect但我想先使其工作,然后添加安全性),最后将elf标头条目添加到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处。 elf标头入口点返回0x5c0(我用十进制的256补偿它)。

此外,pmap显示了一个在假设0x7fdf94d0c000 处创建的可执行区域,因此我获得的函数指针方向(在0x7fdf94d0c6c0处调用)应该是入口点并且可以调用通过函数指针。但是我得到的是,您猜对了:segfault。

我想指出的是,我有一个与(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");
}

1 个答案:

答案 0 :(得分:3)

  

它要求我使用mmap技巧。我已经看到我的教授成功实现了它,但是我不知道如何实现。

要使其正常工作,您的__start必须完全独立,并且调用任何其他库(您通过调用strcmp和{{1违反了该要求}}。

当您调用一个未解析的符号时,您询问动态加载程序以解析该符号,并且加载程序需要各种信息。该信息通常是在printf调用期间设置的。由于您绕过了dlopen,因此程序崩溃根本就不足为奇了。

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

第二,在该代码中添加一个崩溃:

__start

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

第三,删除崩溃的代码并仅使用 直接系统调用(例如void __start(...) { int *p = NULL; *p = 42; // should crash here } (但不要调用write(1, "Hello\n", 6) -您必须直接在库中实现它) ))实现您所需的一切。