正在运行的C程序可以访问自己的符号表吗?

Jim*_*eck 12 c linux gcc elf symbol-tables

我有一个linux C程序来处理发送到TCP套接字(绑定到特定端口)的请求.我希望能够通过对该端口的请求来查询C程序的内部状态,但我不想硬编码可以查询的全局变量.因此,我希望查询包含全局字符串名称和C代码,以在符号表中查找该字符串以查找其地址,然后通过TCP套接字将其值发回.当然,符号表不得被剥离.那么C程序甚至可以找到自己的符号表,是否有一个库接口用于查找给出其名称的符号?这是一个用gcc构建的ELF可执行C程序.

Die*_*Epp 13

这实际上相当容易.您使用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;
}
Run Code Online (Sandbox Code Playgroud)

要将所有符号添加到动态符号表,请使用-Wl,--export-dynamic.如果要从符号表中删除大多数符号(推荐),请设置-fvisibility=hidden然后显式添加所需的符号__attribute__((visibility("default")))或其他方法之一.

~ $ gcc dlopentest.c -Wall -Wextra -ldl
~ $ ./a.out A B C
A = (null)
B = (null)
C = (null)
~ $ gcc dlopentest.c -Wall -Wextra -ldl -Wl,--export-dynamic
~ $ ./a.out A B C
A = Value of A
B = (null)
C = Value of C
~ $ gcc dlopentest.c -Wall -Wextra -ldl -Wl,--export-dynamic -fvisibility=hidden
~ $ ./a.out A B C
A = Value of A
B = (null)
C = (null)

安全

请注意,有很多不良行为的空间.

$ ./a.out printf
printf = ???? (garbage)

如果您希望这是安全的,您应该创建允许符号的白名单.


yan*_*wen 7

file:reflect.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;
}
Run Code Online (Sandbox Code Playgroud)

file:reflect.h

#include <stdio.h>

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

void * reflect_query_symbol(const char *name);
Run Code Online (Sandbox Code Playgroud)

file:main.c

只需#include"reflect.h"并调用reflect_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;
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)