Как я могу отладить процесс, связанный с библиотекой времени выполнения?

Сейчас я изучаю взаимодействие библиотек с C в Ubuntu 18.04 и тестирую два простых кода для обертывания strlen: "mystrlen.c", "mystrlenTest.c".

Вот код, который я написал: mystrlen.c

#ifdef RUNTIME
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>

/* strlen wrapper function */
size_t strlen(const char *str) {
    size_t (*strlenp)(const char *) = NULL;
    printf("%s\n", *str);

    strlenp = dlsym(RTLD_NEXT, "strlen");   // Get address of libc strlen
    printf("length: %ld\n", strlenp(str));

    return strlenp(str);
}
#endif

И mystrlenTest.c:

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

int main(void) {
    char testString[] = "Hello World!";
    printf("length of the testString: %ld\n", strlen(testString));

    return 0;
}

Я попытался выполнить инъекцию во время выполнения с помощью следующей команды:

$ gcc -DRUNTIME -shared -fpic -g -o mystrlen.so mystrlen.c -ldl
$ gcc -g -o mystrlenTest mystrlenTest.c
$ LD_PRELOAD=./mystrlen.so ./mystrlenTest

И это все, что я получаю: Segmentation fault (core dumped)Так что я попробовал dmesg команда, чтобы узнать, что произошло, и результат был таким, как показано ниже:

[842291.658267] mystrlenTest[51446]: segfault at 48 ip 00007f7b918e35a1 sp 00007ffdd7158c88 error 4 in libc-2.27.so[7f7b91755000+1e7000]
[842291.658272] Code: 2e 0f 1f 84 00 00 00 00 00 31 c0 c5 f8 77 c3 66 2e 0f 1f 84 00 00 00 00 00 89 f9 48 89 fa c5 f9 ef c0 83 e1 3f 83 f9 20 77 1f <c5> fd 74 0f c5 fd d7 c1 85 c0 0f 85 df 00 00 00 48 83 c7 20 83 e1

Я хотел спросить, как мне отладить это с помощью GDB? Или есть другой способ отладить? Я знаю, как отлаживать отдельную программу с помощью gdb, но у меня возникают трудности с отладкой встроенного процесса библиотеки времени выполнения. Если я смогу узнать, что в00007f7b918e35a1, это было бы большим подспорьем.

2 ответа

Решение

При вызове gdb вы можете передать переменные среды, которые хотите установить, например:

gdb --args env LD_PRELOAD=./mystrlen.so ./mystrlenTest

Теперь вы можете использовать get the backtrace как обычно в gdb.

Вы также можете использовать системные вызовы для печати отладочных сообщений, например:

write(2, "message", sizeof "message" - 1);

Что вообще не вызывает никаких библиотечных функций. (Хотя у большинства системных вызовов есть тонкие оболочки в glibc, для пользователей это в значительной степени не имеет значения). В вашем конкретном случае вероятная проблема заключается в том, чтоprintf сам звонит strlen как заметил Эндрю Хенле, что приводит к бесконечной рекурсии.

При вставке вам необходимо внимательно относиться к тому, что вы вызываете из вставленных функций, и всегда спрашивать себя, могут ли какие-либо вызовы из вставленной функции привести к обратному вызову той же вставленной функции или любой другой вставленной функции (-ям) и предпринять необходимые шаги для заниматься такими случаями. Например, многие стандартные функции могут внутренне выделять память черезmalloc семейные функции и если вы вмешиваетесь malloc и друзья, это может вызвать непредвиденные проблемы.

Учитывая этот код:

size_t strlen(const char *str) {
    size_t (*strlenp)(const char *) = NULL;
    printf("%s\n", *str);

    strlenp = dlsym(RTLD_NEXT, "strlen");   // Get address of libc strlen
    printf("length: %ld\n", strlenp(str));

    return strlenp(str);
}

Если printf() использует strlen() внутри вы получите бесконечную рекурсию и почти наверняка нарушение сегментации.

Обратите внимание, что в GLIBC libc.so используется в Linux, printf()реализуется через vfprintf(), который действительно использует strlen().

Другие вопросы по тегам