如何从共享对象中找到程序的argc
和argv
?我正在用C语言编写一个将通过LD_PRELOAD
加载的库。我已经能够以两种不同的方式找到堆栈:
rsp
来电阅读__asm__
。/proc/<pid>/maps
并解析堆栈条目。然后我可以创建一个指针,将其指向堆栈段,然后迭代查找数据。问题是我无法找到一种有效的方法来确定哪些字节是argc
以及指向argv
字符串的指针。
我知道/proc/<pid>/cmdline
也包含参数,每个参数由0x00
分隔,但我有兴趣在内存中找到所有内容。
在gdb中,我看到DWORD
为argc
,后跟QWORD
,这是第一个指针。 argc
地址之前的20个字节是指向主程序代码段的指针。但这不是确定argc
和argv
的确定性方法。
我看过一些帖子但没有工作代码:
答案 0 :(得分:13)
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
的调用,并且明确调用了argc
和argv
稍后会传递给main
(以及environ
的值)。
答案 1 :(得分:0)
最可靠的可能是使用/proc/<pid>/cmdline
,因为它是由内核提供的,并且不会根据C实现而改变(例如,它取决于您正在使用的处理器)。
问题是在某些平台上,函数的参数(fx main
)将在堆栈上传递,但在其他平台上,它可能作为寄存器传递(fx在x86-64平台上)。如果它是通过寄存器发送的,那么如果启用了优化,main
将不将这些存储在内存中(如果不需要) - 如果不这样做,它可能不会留在内存中明确地这样做。
即使参数在堆栈上传递,main
的参数所在的确切位置也可能因编译器/实现的版本而异。这意味着几乎没有任何可靠的方法从堆栈中检索它们(并且有人指出它们可能在执行main
期间被修改,作为命令行解析的一部分)。
即使内核将参数传递给程序也没有多大帮助,因为它们通过寄存器传递 - 这意味着它们将被存储的位置完全取决于CRT init(反过来可能会改变)从版本到版本。)
简而言之,稍后检索argv
和argc
需要您使用的CRT的明确支持(Microsoft的CRT会这样做,但GNU不会使用AFAIK)。
你 当然要做的就是获取GCC的来源并修补CRT init,以便将argv
和argc
实际存储在稍后可以检索它们的地方。如果您需要在运行程序的CRT init之前访问它们(在动态链接期间为fx),那当然不会起作用。
答案 2 :(得分:0)
这是个坏主意,但我还没有天真地说你没有正当的理由。
如果您只知道堆栈的位置,就没有找到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;
}
}