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