从库中查找 argc 和 argv

Find argc and argv from a library

如何从共享对象中找到程序的 argcargv?我正在用 C 编写一个库,它将通过 LD_PRELOAD 加载。我已经能够通过两种不同的方式找到堆栈:

  1. 通过内联 __asm__ 调用读取 rsp
  2. 读取 /proc/<pid>/maps 并解析堆栈条目。

然后我可以创建一个指针,将其指向堆栈段,然后循环查找数据。问题是我想不出一种有效的方法来确定哪些字节是 argc 以及指向 argv 字符串的指针。

我知道 /proc/<pid>/cmdline 也包含参数,每个参数由 0x00 分隔,但我有兴趣在内存中找到所有内容。

在 gdb 中,我看到 argcDWORD 后跟第一个指针 QWORDargc 地址之前的 20 个字节是指向主程序代码段的指针。但这不是识别 argcargv 的确定性方法。

我看过一些帖子,但没有工作代码:

This response 在你的第二个 link 中包含对我来说工作正常的工作源代码(Gnu/Linux elf-based 系统),包括在 LD_PRELOAD.

代码很短;它由一个函数组成:

int foo(int argc, char **argv, char **env) {
   // Do something with argc, argv (and env, if desired)
}

和指向 .init_array 部分中该函数的指针:

__attribute__((section(".init_array"))) static void *foo_constructor = &foo;

将其放入共享库,然后 LD_PRELOADing 共享库在我尝试时确实触发了对 foo 的调用,并且显然是用 argcargv 稍后将传递给 main(以及 environ 的值)。

最可靠的可能是使用 /proc/<pid>/cmdline,因为它由内核提供,不会根据 C 实现而改变(例如,它取决于您使用的处理器)。

问题在于,在某些平台上,函数的参数 (fx main) 将在堆栈上传递,但在其他平台上,它可能作为寄存器传递(x86-64 平台上的 fx) .如果它是通过寄存器发送的,那么如果启用了优化,main 不会 在不需要的情况下将它们存储在内存中——也就是说,如果你这样做,它很可能不会保留在内存中不要自己明确地这样做。

即使参数在堆栈上传递,main 的参数所在的确切位置也可能因 compiler/implementation 的版本而异。这意味着几乎没有任何可靠的方法可以从堆栈中检索它们(正如有人指出的那样,它们可能会在执行 main 期间作为命令行解析的一部分进行修改)。

即使内核将参数传递给程序的方式也无济于事,因为它们是通过寄存器传递的——这意味着它们的存储位置完全取决于 CRT init(反过来可能会因版本而异。

简而言之,稍后检索 argvargc 需要您使用的 CRT 的明确支持(Microsoft 的 CRT 支持,但据我所知,GNU 不支持)。

可以 做的当然是获取 GCC 的源代码并修补 CRT init 以实际存储 argvargc您可以稍后检索它们。如果您需要在程序的 CRT 初始化之前访问它们,那当然是行不通的 运行(动态链接期间的 fx)。

这是个坏主意,但我还没有天真到说你没有正当理由。

如果您只知道堆栈的位置,就没有找到 argc/argv 的好方法。幸运的是,envp 直接在堆栈上的 argv 之后,并且我所知道的每个 libc 都将 envp 放在 __environ 全局中。所以从 __environ 向后,你可以找到 argc 和 argv。下面是一些用 Rust 编写的示例代码,应该很容易移植到 C++:

extern "C" {
    pub static __environ: *const *const c_char;
}

fn raw_args() -> (c_int, *const *const c_char) {
    let mut walk_environ = unsafe { __environ as *const usize };
    walk_environ = walk_environ.wrapping_offset(-1);
    let mut i = 0;

    loop {
        let argc_ptr = walk_environ.wrapping_offset(-1) as *const c_int;
        let argc = unsafe { *argc_ptr };
        if argc == i {
            break (argc, walk_environ as *const *const c_char);
        }
        walk_environ = walk_environ.wrapping_offset(-1);
        i += 1;
    }
}