【问题标题】:Find argc and argv from a library从库中查找 argc 和 argv
【发布时间】:2016-01-21 05:14:54
【问题描述】:

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

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

然后我可以创建一个指针,将它指向堆栈段,然后遍历查找数据。问题是我无法找到一种有效的方法来确定哪些字节是argc 以及指向argv 字符串的指针的指针。

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

在 gdb 中,我看到 DWORD 代表 argc,然后是 QWORD,这是第一个指针。 argc 地址前 20 个字节是一个指向主程序代码段的指针。但这不是识别argcargv 的确定方法。

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

【问题讨论】:

  • 似乎有点古怪,取决于编译器如何使用堆栈。一旦有人找到编译器/运行时优化,这很可能会改变。应用程序可能还希望以不同的方式使用相同的 args,如果您的 lib 尝试解释未针对它的参数,这可能会导致问题。您不能在“构造函数”调用中将这些直接传递给您的库吗?是的,我知道您想要做的是避免这种开销。
  • 什么时候可以访问argcargv?在LD_PRELOAD 阶段可能是不可能的。
  • 程序修改argv中的数据也是完全合法的。我不确定在这种情况下堆栈会发生什么。
  • @ChrisR:到底是什么开销?调用初始化程序?
  • @rici - 是的,编程开销,我应该明确表示。代码实际上可能运行得更快。

标签: c++ c linux pointers argv


【解决方案1】:

您第二个链接中的This response 包含对我来说很好的工作源代码(基于 Gnu/Linux elf 的系统),包括在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 调用的,稍后将传递给@987654329 @(以及 environ 的值)。

【讨论】:

  • 很好的答案!我从没想过在库中以这种方式运行构造函数。无需摆弄编译器或运行时依赖项。爱它。我每天都在学习。
【解决方案2】:

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

如果您只知道堆栈的位置,则没有找到 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;
    }
}

【讨论】:

    【解决方案3】:

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

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

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

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

    简而言之,稍后检索 argvargc 需要您正在使用的 CRT 的明确支持(Microsoft 的 CRT 可以做到这一点,但 GNU 不支持 AFAIK)。

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

    【讨论】:

      猜你喜欢
      • 2013-09-08
      • 2011-04-15
      • 2012-04-20
      • 2018-11-30
      • 1970-01-01
      • 2020-11-03
      • 2013-05-27
      • 2015-03-27
      • 2013-02-02
      相关资源
      最近更新 更多