mmap загружает общий объект и получает указатель на функцию - PullRequest
0 голосов
/ 14 ноября 2018

Я хочу динамически загрузить библиотеку без использования функций из dlfcn.h У меня есть папка, полная .so файлов, скомпилированных с:

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

И все они имеют функцию входа с именем:

void __start(int size, char** cmd);

(я знаю, наверное, не лучшее имя)

затем я пытаюсь вызвать open поверх так, затем прочитать заголовок эльфа как Elf64_Ehdr загрузить его в память с помощью 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, я получаю, что _start на 0x6c0. Точка входа заголовка elf возвращает 0x5c0 (я компенсирую это добавлением 256 в декабрь).

Также pmap показывает исполняемую область, создаваемую, скажем, 0x7fdf94d0c000 , поэтому направление указателя функции, которое я получаю и которое я вызываю, находится на 0x7fdf94d0c6c0, это должна быть точка входа и вызываемая через указатель функции. Но то, что я получаю, вы уже догадались: segfault.

Еще одна вещь, на которую я хотел бы обратить внимание, это то, что у меня работает тот же пример с (dlopen, dlsym, dlclose), но необходимо использовать трюк mmap. Я видел, как мой профессор успешно его применяет, но я не могу понять, как это сделать. (возможно, есть более простой способ, который мне не хватает).

Я основал код на этом примере jit

Заранее спасибо!

Редактировать: это код .c, который я компилирую в .so:

#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 голосов
/ 15 ноября 2018

требуется, чтобы я использовал трюк mmap.Я видел, как мой профессор успешно его реализовал, но я не могу понять, как.

Чтобы это работало, ваш __start должен быть полностью автономным, а не вызывать любойдругая библиотека (вы нарушили это требование, вызвав strcmp и printf).

В тот момент, когда вы вызываете неразрешенный символ, вы просите динамический загрузчик разрешить этот символ, и загрузчик нуждается во всех видах информации для этого.Эта информация обычно устанавливается во время dlopen вызова.Поскольку вы обошли dlopen, вовсе не удивительно, что ваша программа вылетает.

Ваш первый шаг должен состоять в том, чтобы изменить код так, чтобы __start ничего не делал привсе (пусто), и убедитесь, что вы можете позвонить __start.Это подтвердит, что вы правильно вычисляете его адрес.

Во-вторых, добавьте сбой к этому коду:

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

и убедитесь, что вы наблюдаете сбой сейчас.Это подтвердит, что ваш код вызывается.

В-третьих, удалите код сбоя и используйте только прямые системные вызовы (например, write(1, "Hello\n", 6) (но не вызывайте write -Вы должны реализовать это непосредственно в своей библиотеке)), чтобы реализовать все, что вам нужно.

...