Может ли работающая программа C получить доступ к своей собственной таблице символов?

У меня есть программа на Linux, которая обрабатывает запрос, отправляемый на сокет TCP (привязанный к определенному порту). Я хочу иметь возможность запрашивать внутреннее состояние программы на C через запрос к этому порту, но я не хочу жестко кодировать, какие глобальные переменные могут быть запрошены. Таким образом, я хочу, чтобы запрос содержал строковое имя глобала и код C, чтобы найти эту строку в таблице символов, чтобы найти ее адрес, а затем отправить ее значение обратно через сокет TCP. Конечно, таблица символов не должна быть удалена. Так может ли программа C даже найти свою собственную таблицу символов, и есть ли интерфейс библиотеки для поиска символов по их имени? Это исполняемая ELF-программа на C, созданная с помощью gcc.

3 ответа

Решение

Это на самом деле довольно легко. Ты используешь dlopen / dlsym для доступа к символам. Чтобы это работало, символы должны присутствовать в динамической таблице символов. Есть несколько таблиц символов!

#include <dlfcn.h>
#include <stdio.h>

__attribute__((visibility("default")))
const char A[] = "Value of A";

__attribute__((visibility("hidden")))
const char B[] = "Value of B";

const char C[] = "Value of C";

int main(int argc, char *argv[])
{
    void *hdl;
    const char *ptr;
    int i;

    hdl = dlopen(NULL, 0);
    for (i = 1; i < argc; ++i) {
        ptr = dlsym(hdl, argv[i]);
        printf("%s = %s\n", argv[i], ptr);
    }
    return 0;
}

Чтобы добавить все символы в таблицу динамических символов, используйте -Wl,--export-dynamic, Если вы хотите удалить большинство символов из таблицы символов (рекомендуется), установите -fvisibility=hidden а затем явно добавьте символы, которые вы хотите с __attribute__((visibility("default"))) или один из других методов.

~ $ gcc dlopentest.c -Wall -Wextra -ldl
~ $ ./a.out A B C
A = (ноль)
B = (ноль)
C = (ноль)
~ $ gcc dlopentest.c -Wall -Wextra -ldl -Wl,- экспорт-динамический
~ $ ./a.out A B C
A = значение A
B = (ноль)
C = значение C
~ $ gcc dlopentest.c -Wall -Wextra -ldl -Wl,- export-dynamic -fvisibility= скрыто
~ $ ./a.out A B C
A = значение A
B = (ноль)
C = (ноль)

безопасности

Обратите внимание, что есть много места для плохого поведения.

$./a.out printf
printf = ▯▯▯▯ (мусор)

Если вы хотите, чтобы это было безопасно, вы должны создать белый список допустимых символов.

Файл: refle.c

#include <stdio.h>
#include "reflect.h"

struct sym_table_t gbl_sym_table[1] __attribute__((weak)) = {{NULL, NULL}};

void * reflect_query_symbol(const char *name)
{
    struct sym_table_t *p = &gbl_sym_table[0];

    for(; p->name; p++) {
        if(strcmp(p->name, name) == 0) {
            return p->addr;
        }
    }
    return NULL;
}

файл: refle.h

#include <stdio.h>

struct sym_table_t {
    char *name;
    void *addr;
};

void * reflect_query_symbol(const char *name);

файл: main.c

просто #include "refle.h" и вызовите refle_query_symbol

пример:

#include <stdio.h>
#include "reflect.h"

void foo(void)
{
    printf("bar test\n");
}

int uninited_data;

int inited_data = 3;

int main(int argc, char *argv[])
{
    int i;
    void *addr;

    for(i=1; i<argc; i++) {
        addr = reflect_query_symbol(argv[i]);
        if(addr) {
            printf("%s lay at: %p\n", argv[i], addr);
        } else {
            printf("%s NOT found\n", argv[i], addr);
        }
    }

    return 0;
}

Файл:Makefile

objs = main.o reflect.o

main: $(objs)
        gcc -o $@ $^
        nm $@ | awk 'BEGIN{ print "#include <stdio.h>"; print "#include \"reflect.h\""; print "struct sym_table_t gbl_sym_table[]={" } { if(NF==3){print "{\"" $$3 "\", (void*)0x" $$1 "},"}} END{print "{NULL,NULL} };"}' > .reflect.real.c
        gcc -c .reflect.real.c -o .reflect.real.o
        gcc -o $@ $^ .reflect.real.o
        nm $@ | awk 'BEGIN{ print "#include <stdio.h>"; print "#include \"reflect.h\""; print "struct sym_table_t gbl_sym_table[]={" } { if(NF==3){print "{\"" $$3 "\", (void*)0x" $$1 "},"}} END{print "{NULL,NULL} };"}' > .reflect.real.c
        gcc -c .reflect.real.c -o .reflect.real.o
        gcc -o $@ $^ .reflect.real.o

Общий термин для такого рода функций - " отражение", и он не является частью C.

Если это для целей отладки, и вы хотите иметь возможность удаленно проверять все состояние программы на C, проверять любую переменную, запускать и останавливать ее выполнение и т. Д., Вы можете рассмотреть возможность удаленной отладки GDB:

GDB предлагает "удаленный" режим, часто используемый при отладке встроенных систем. Удаленная работа - это когда GDB работает на одном компьютере, а отлаживаемая программа работает на другом. GDB может связываться с удаленной заглушкой, которая понимает протокол GDB через последовательный порт или TCP/IP. Программа-заглушка может быть создана путем ссылки на соответствующие файлы-заглушки, предоставляемые GDB, которые реализуют целевую сторону протокола связи. В качестве альтернативы, gdbserver может использоваться для удаленной отладки программы без необходимости ее изменения.

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