重载运行进程的符号(LD_PRELOAD附件)

 fuckyourgirlfriend 发布于 2022-12-09 16:40

我正在研究Linux的堆分析器,称为heaptrack.目前,我依赖于LD_PRELOAD重载各种(解)分配函数,这非常有效.

现在我想扩展该工具以允许运行时附加到现有进程,该进程是在没有LD_PRELOAD我的工具的情况下启动的.我可以dlopen通过GDB很好地使用我的库,但是这不会覆盖malloc等等.我认为,这是因为此时链接器已经解决了已经运行的进程的位置相关代码 - 正确吗?

那么我该怎么办才能超载malloc和朋友呢?

我不熟悉汇编代码.从我到目前为止所读到的,我想我将以某种方式必须修补malloc和其他功能,以便他们首先回调我的跟踪功能,然后继续他们的实际实现?那是对的吗?我怎么做?

我希望有现有的工具,或者我可以利用GDB/ptrace.

1 个回答
  • 只是为了lulz,另一个解决方案,没有你自己的过程或触摸单一的装配线或玩弄/proc.您只需要在流程的上下文中加载库,让魔法发生.

    我建议的解决方案是使用构造函数功能(通过gcc从C++引入C)在加载库时运行一些代码.然后这个库只修补GOT(全局偏移表)条目malloc.GOT存储库函数的实际地址,以便名称解析只发生一次.要修补GOT,你必须使用ELF结构(参见参考资料man 5 elf).Linux非常友好,可以为您提供aux向量(请参阅参考资料man 3 getauxval),告诉您在内存中找到当前程序的程序头的位置.但是,提供了更好的接口dl_iterate_phdr,下面使用它.

    下面是一个库的示例代码,它在init调用函数时完成此操作.虽然使用gdb脚本可能会实现相同的目的.

    #define _GNU_SOURCE
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <dlfcn.h>
    #include <sys/auxv.h>
    #include <elf.h>
    #include <link.h>
    #include <sys/mman.h>
    
    
    struct strtab {
        char *tab;
        ElfW(Xword) size;
    };
    
    
    struct jmpreltab {
        ElfW(Rela) *tab;
        ElfW(Xword) size;
    };
    
    
    struct symtab {
        ElfW(Sym) *tab;
        ElfW(Xword) entsz;
    };
    
    
    
    /* Backup of the real malloc function */
    static void *(*realmalloc)(size_t) = NULL;
    
    
    /* My local versions of the malloc functions */
    static void *mymalloc(size_t size);
    
    
    /*************/
    /* ELF stuff */
    /*************/
    static const ElfW(Phdr) *get_phdr_dynamic(const ElfW(Phdr) *phdr,
            uint16_t phnum, uint16_t phentsize) {
        int i;
    
        for (i = 0; i < phnum; i++) {
            if (phdr->p_type == PT_DYNAMIC)
                return phdr;
            phdr = (ElfW(Phdr) *)((char *)phdr + phentsize);
        }
    
        return NULL;
    }
    
    
    
    static const ElfW(Dyn) *get_dynentry(ElfW(Addr) base, const ElfW(Phdr) *pdyn,
            uint32_t type) {
        ElfW(Dyn) *dyn;
    
        for (dyn = (ElfW(Dyn) *)(base + pdyn->p_vaddr); dyn->d_tag; dyn++) {
            if (dyn->d_tag == type)
                return dyn;
        }
    
        return NULL;
    }
    
    
    
    static struct jmpreltab get_jmprel(ElfW(Addr) base, const ElfW(Phdr) *pdyn) {
        struct jmpreltab table;
        const ElfW(Dyn) *dyn;
    
        dyn = get_dynentry(base, pdyn, DT_JMPREL);
        table.tab = (dyn == NULL) ? NULL : (ElfW(Rela) *)dyn->d_un.d_ptr;
    
        dyn = get_dynentry(base, pdyn, DT_PLTRELSZ);
        table.size = (dyn == NULL) ? 0 : dyn->d_un.d_val;
        return table;
    }
    
    
    
    static struct symtab get_symtab(ElfW(Addr) base, const ElfW(Phdr) *pdyn) {
        struct symtab table;
        const ElfW(Dyn) *dyn;
    
        dyn = get_dynentry(base, pdyn, DT_SYMTAB);
        table.tab = (dyn == NULL) ? NULL : (ElfW(Sym) *)dyn->d_un.d_ptr;
        dyn = get_dynentry(base, pdyn, DT_SYMENT);
        table.entsz = (dyn == NULL) ? 0 : dyn->d_un.d_val;
        return table;
    }
    
    
    
    static struct strtab get_strtab(ElfW(Addr) base, const ElfW(Phdr) *pdyn) {
        struct strtab table;
        const ElfW(Dyn) *dyn;
    
        dyn = get_dynentry(base, pdyn, DT_STRTAB);
        table.tab = (dyn == NULL) ? NULL : (char *)dyn->d_un.d_ptr;
        dyn = get_dynentry(base, pdyn, DT_STRSZ);
        table.size = (dyn == NULL) ? 0 : dyn->d_un.d_val;
        return table;
    }
    
    
    
    static void *get_got_entry(ElfW(Addr) base, struct jmpreltab jmprel,
            struct symtab symtab, struct strtab strtab, const char *symname) {
    
        ElfW(Rela) *rela;
        ElfW(Rela) *relaend;
    
        relaend = (ElfW(Rela) *)((char *)jmprel.tab + jmprel.size);
        for (rela = jmprel.tab; rela < relaend; rela++) {
            uint32_t relsymidx;
            char *relsymname;
            relsymidx = ELF64_R_SYM(rela->r_info);
            relsymname = strtab.tab + symtab.tab[relsymidx].st_name;
    
            if (strcmp(symname, relsymname) == 0)
                return (void *)(base + rela->r_offset);
        }
    
        return NULL;
    }
    
    
    
    static void patch_got(ElfW(Addr) base, const ElfW(Phdr) *phdr, int16_t phnum,
            int16_t phentsize) {
    
        const ElfW(Phdr) *dphdr;
        struct jmpreltab jmprel;
        struct symtab symtab;
        struct strtab strtab;
        void *(**mallocgot)(size_t);
    
        dphdr = get_phdr_dynamic(phdr, phnum, phentsize);
        jmprel = get_jmprel(base, dphdr);
        symtab = get_symtab(base, dphdr);
        strtab = get_strtab(base, dphdr);
        mallocgot = get_got_entry(base, jmprel, symtab, strtab, "malloc");
    
        /* Replace the pointer with our version. */
        if (mallocgot != NULL) {
            /* Quick & dirty hack for some programs that need it. */
            /* Should check the returned value. */
            void *page = (void *)((intptr_t)mallocgot & ~(0x1000 - 1));
            mprotect(page, 0x1000, PROT_READ | PROT_WRITE);
            *mallocgot = mymalloc;
        }
    }
    
    
    
    static int callback(struct dl_phdr_info *info, size_t size, void *data) {
        uint16_t phentsize;
        data = data;
        size = size;
    
        printf("Patching GOT entry of \"%s\"\n", info->dlpi_name);
        phentsize = getauxval(AT_PHENT);
        patch_got(info->dlpi_addr, info->dlpi_phdr, info->dlpi_phnum, phentsize);
    
        return 0;
    }
    
    
    
    /*****************/
    /* Init function */
    /*****************/
    __attribute__((constructor)) static void init(void) {
        realmalloc = malloc;
        dl_iterate_phdr(callback, NULL);
    }
    
    
    
    /*********************************************/
    /* Here come the malloc function and sisters */
    /*********************************************/
    static void *mymalloc(size_t size) {
        printf("hello from my malloc\n");
        return realmalloc(size);
    }
    

    一个示例程序只是在两次malloc调用之间加载库.

    #include <stdio.h>
    #include <stdlib.h>
    #include <dlfcn.h>
    
    
    
    void loadmymalloc(void) {
        /* Should check return value. */
        dlopen("./mymalloc.so", RTLD_LAZY);
    }
    
    
    
    int main(void) {
        void *ptr;
    
        ptr = malloc(42);
        printf("malloc returned: %p\n", ptr);
    
        loadmymalloc();
    
        ptr = malloc(42);
        printf("malloc returned: %p\n", ptr);
    
        return EXIT_SUCCESS;
    }
    

    呼叫mprotect通常是无用的.但是我发现gvim(编译为共享对象)需要它.如果您还想捕获对malloc指针的引用(可能允许稍后调用实际函数并绕过您的指针),则可以将相同的进程应用于DT_RELA动态条目指向的符号表.

    如果构造函数功能不可用,您所要做的就是init从新加载的库中解析符号并调用它.

    请注意,您可能还想要替换,dlopen以便在您的库之后加载的库也得到修补.如果您很早加载库或者应用程序已动态加载插件,可能会发生这种情况.

    2022-12-11 03:12 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有