如何在运行时在 64 位二进制文​​件中找到环境变量的位置?

逆向工程 linux 动态分析 x64
2021-06-25 14:04:57

在main函数中,程序作者可以选择接收char *数组作为指向环境变量的main的参数

int main(int argc, char **argv, char **envp) {
...
}

这里的重点是需要 envp 指针,但不是从程序作者和源代码的角度来看。问题是,在运行时,即使正在运行的二进制文件被剥离,如何知道环境变量的位置?

基本上,我已经想出了如何在 32 位和 64 位 linux 上为 argv 可靠地做到这一点;我可以知道有多少命令参数,它们在哪里以及它们是什么。我可以在 32 位上为 envp 执行此操作,但不能在 64 位上执行此操作。有没有人知道一种技术可以在运行时在 linux 中对剥离的二进制文件可靠地为 64 位执行此操作?

2个回答

全局变量__environ保存一个指向列表开头的指针。在我的测试中*它是由 libm.so 导出的

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

// Slightly modded from http://linux.die.net/man/3/dlopen
static char*** _findEnviron()
{
    void *handle;
        char *error,
        ***ret = NULL; //! Pointer to a ** list

    handle = dlopen("libm.so", RTLD_LAZY);
        if (!handle) {
        fprintf(stderr, "%s\n", dlerror());
        return NULL;
        }

    dlerror();    /* Clear any existing error */

    ret = dlsym(handle, "__environ");

    if ((error = dlerror()) != NULL)  {
        fprintf(stderr, "%s\n", error);
        dlclose(handle);
        return NULL;
    }

    dlclose(handle);

    return ret;
}

void print(char** p)
{
    while(p && *p)
    {
        printf("%p --> %s\n", *p, *p);
        ++p;
    }
}

int main(int argc, char** argv, char** envp)
{
    char** pEnviron = NULL;

    printf("Find %p\n", envp);
    pEnviron = *_findEnviron();
    printf("Found %p\n", pEnviron);

    print(envp);
    printf("---\n");
    print(pEnviron);

    getc(stdin);
}
    1. Debian 2.6.32;ldd (Debian EGLIBC 2.13-38+deb7u8) 2.13
    2. CentOS7 3.10.0;ldd (GNU libc) 2.17

事实证明——虽然这并不完全构成一个可靠的、交叉编译器、跨操作系统、跨 32/64 位解决方案,但它适用于我的机器——它们在堆栈上,如果你导航刚过栈上的argc可以看到环境变量的指针是argc*sizeof(uintptr_t)+argv,argv自然就和argc相邻了。

您可以通过连续执行 strcpy 以编程方式读取环境变量(我知道这是垃圾 api,但它们null 终止)。等到你得到一个空字节并且根本没有字符串——这表示结束。

这与我可能希望的答案质量不同,但现在就足够了。