对argv可能的误解
LI Rui

最近看到了一篇关于人们可能对argv内容有误解的文章,这里进行一个C语言的实践。

首先我们的callee.c内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

int main(int argc, char *argv[], char *envp[])
{
printf("argc: %d\n", argc);
for (int i = 0; i < argc; i++)
printf("argv[%d]: %s\n", i, argv[i]);
printf("envp[0]: %s\n", envp[0]);
printf("====================================================================\n");
printf("envp = %p\targc + argv + 1 = %p\n", envp, argc + argv + 1);
printf("argv: %p value: %p\n", argv, *argv);
printf("envp: %p value: %p\n", envp, *envp);
for (int i = 0; i < argc; i++)
printf("argv[%d]: %p\n", i, argv[i]);
for (int i = 0; envp[i] != NULL; i++)
printf("envp[%d]: %p\n", i, envp[i]);
return 0;
}

直接执行的输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ ./callee
argc: 1
argv[0]: ./callee
envp[0]: SHELL=/bin/zsh
====================================================================
envp = 0x7ffd190083f8 argc + argv + 1 = 0x7ffd190083f8
argv: 0x7ffd190083e8 value: 0x7ffd19009909
envp: 0x7ffd190083f8 value: 0x7ffd19009912
argv[0]: 0x7ffd19009909
envp[0]: 0x7ffd19009912
envp[1]: 0x7ffd19009921
envp[2]: 0x7ffd19009999
...

这里我们的argv[0]为程序的filename,而envp内容为当前的环境变量。同时以下等式成立:

即envp的地址为argc(参数个数)+argv首地址+1(argv结束的NULL),说明argv结束后紧接着就是envp。

但是我们知道Linux中我们可以直接调用execve接口将当前执行的程序覆盖为新的程序,同时指定argv和envp。

1
2
3
#include <unistd.h>

int execve(const char *pathname, char *const argv[], char *const envp[]);

于是就有了我们下面的caller.c

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <unistd.h>

int main() {
char *argv[] = { "WHO AM I" };
char *envp[] = { "BAD VALUE", NULL };
execve("./callee", argv, envp);
// Should never return
perror("execve");
return 1;
}

执行的输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
$ ./caller
argc: 2
argv[0]: WHO AM I
argv[1]: BAD VALUE
envp[0]: BAD VALUE
====================================================================
envp = 0x7ffd6747db90 argc + argv + 1 = 0x7ffd6747db90
argv: 0x7ffd6747db78 value: 0x7ffd6747dfd2
envp: 0x7ffd6747db90 value: 0x7ffd6747dfe5
argv[0]: 0x7ffd6747dfd2
argv[1]: 0x7ffd6747dfdb
envp[0]: 0x7ffd6747dfe5

这里我们可以发现argv[0]不再是程序的filename,而是一个自定义的值。

因为下面原因:

  • argv和envp都以NULL结尾,但是argv在传参时没有强制要求NULL,而envp如果末尾不为NULL就会出错。
  • envp本来要求格式为key=value,但实际上不会校验。

所以我们的argv数组找到的NULL其实是envp的NULL,这里我们argv包含了argv[0]和envp[0]。这说明:

  • 靠argv[0]判断filename可能是不准确的。
  • envp数组可以成为argv的一部分,需要小心。
  • 本文标题:对argv可能的误解
  • 本文作者:LI Rui
  • 创建时间:2022-01-29 13:21:21
  • 本文链接:https://www.lirui.tech/post/2022/3c1f2a239028.html
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-SA 许可协议。转载请注明出处!