argc/argv随机数据/行为

qjp7pelc  于 2023-05-22  发布在  其他
关注(0)|答案(5)|浏览(141)

以下是我的最小可重复示例:

#include <stdio.h>

int main( int argc, char* argv[])
{
    printf (" this is the contents of argc:%d\n",argc);
            
    int i;

    for (i = 0; i < argc ; i++){
       printf(" argv = %d = %s\n",i,argv[i]);
    }
      
    return 0;
}

当我在for循环中将argc改为一个数字时,比如说10,代码在到达10之前就崩溃了:

$ ./argc one two three
 this is the contents of argc:4
 argv = 0 = ./argc
 argv = 1 = one
 argv = 2 = two
 argv = 3 = three
 argv = 4 = (null)
 argv = 5 = SHELL=/bin/bash
 argv = 6 = SESSION_MANAGER=local/wajih:@/tmp/.ICE-unix/1230,unix/wajih:/tmp/.ICE-unix/1230
 argv = 7 = QT_ACCESSIBILITY=1
 argv = 8 = COLORTERM=truecolor
 argv = 9 = XDG_CONFIG_DIRS=/etc/xdg/xdg-ubuntu:/etc/xdg

例如,将for循环中的argc更改为100;我得到一个很长的错误消息,它以这样的结尾:

argv = 54 = GDMSESSION=ubuntu
 argv = 55 = DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
 argv = 56 = LC_NUMERIC=ar_AE.UTF-8
 argv = 57 = _=./argc
 argv = 58 = OLDPWD=/home/wajih
 argv = 59 = (null)
Segmentation fault (core dumped).

我想知道为什么会这样。

b09cbbtk

b09cbbtk1#

打个比方可能更容易理解这里发生的事情。
假设我住在一个又长又窄的房子里。房子被分成10个房间,但它们都是一样大的,而且都排列成一条直线。
假设我对机器人学感兴趣。假设我造了一个小机器人,它可以在我的房子里到处开,给每个房间拍照。因为我家的房间都是排成一条直线的,所以机器人的导航任务相当简单。
一旦我让机器人的软件完美地工作,我要求机器人对我家的所有20个房间进行完整的摄影调查。(哎呀,我犯了一个错误,在那里。)然后机器人开始沿着房子的主轴行驶,依次为每个房间拍照。
在它拍摄了前10个房间的照片后,当机器人穿过房子的端墙时,会发出一声巨响。它的“11号房间”的图片是破碎的木材和石膏。它的“12号房间”的照片是我家尽头外面的花园。但接着又是一声巨响,机器人继续拍照,不知何故,令人惊讶的是,它们看起来又像房子的内部了!
原来这是因为机器人已经 * 开进了我邻居的房子 *,现在正在那里拍照。
从这个小故事中,我们可以学到两件事:
1.如果我的房子里有10个房间,我让我头脑简单的机器人给20个房间拍照,一些奇怪的、不可预测的、错误的事情可能会发生。
1.即使发生的事情是奇怪的,不可预测的,错误的,它的一小部分似乎有某种意义,这取决于环境。在这种情况下,我的机器人对我家的“第15个房间”的图片看起来就像一间卧室,尽管它看起来不像我家的任何卧室,而且那两个人在床上做的事情也不像我家发生的任何事情。
但这个类比的另一个重要方面是,你显然不能依赖任何一个,因为太多的情况是你无法控制的。机器人可能在穿墙时严重损坏了自己,以至于无法继续拍照。如果碰巧有一条街正好经过我家尽头的花园,机器人可能会被卡车碾过。如果刚好经过我家尽头的花园有一个悬崖,机器人可能会掉进海里。等等
C,就像我的故事中头脑简单的机器人一样,没有任何内置的保护措施来防止数组的结尾。如果你试图访问一个10元素数组的第15个元素,你 * 不会 * 得到的通常是一个错误消息,说“数组边界超出”。你得到的是一些奇怪的,不可预测的,错误的东西-除了,根据情况,可能似乎有某种隐藏的含义,这可能会导致你浪费时间试图弄清楚它,或者在Stack Overflow上询问它。但是,与其这样做,你可能想花时间为机器人研究一个更好的障碍物检测或碰撞避免算法。:—)
另请参阅前面关于超出数组界限的SO问题:1234567891011121314

x759pob2

x759pob22#

argv指针在程序内存中有一个非常特定的位置。
当你运行一个二进制文件时,总是有一些入口点。在C中,这是main()函数。但是,为了准备好二进制文件在该位置启动的环境,操作系统必须首先做一些事情。
它必须复制环境变量,从操作系统请求和偏移内存等。因为这个过程是完全确定的(每个OS),所以实际上可以在这些参数之后读取环境变量。

这个原则是计算机安全的基础。如果攻击者设法泄漏此内存段中的指针,则他们可以覆盖某些环境变量(即PATH),以首先指向它们自己的二进制文件。hackmd有一个很好的例子:HackMD: Environment variables attack
图片来源:COMPILER, ASSEMBLER, LINKER AND LOADER: A BRIEF STORY

h79rfbju

h79rfbju3#

您正在调用未定义的行为。C标准说argv[argc]将是一个空指针,并且试图访问argv[i] for i < 0 or i >argc是未定义的行为。
“未定义的行为”意味着任何事情都可能发生。如果你要求解释,没有别的,只有“这是未定义的行为”。编译器在将你所有的钱都汇到我的银行账户后产生完全擦除你的硬盘驱动器的代码是法律的的。别这么做你在做你不被允许做的事情,这就是完整的答案。

a5g8bdjr

a5g8bdjr4#

大多数Unix系统为main函数提供了第三个参数。

int main( int argc, char *argv[], char *envp[]);

它被称为环境变量。在上面的例子中,它打印第三个参数-envp的内容。但它不会总是表现出相同的行为。在argc计数后从argv打印数据具有未定义的行为

pb3s4cty

pb3s4cty5#

在C语言中,超过数组的末尾会给予未定义的行为。您将得到的结果将根据编译器、操作系统、您使用的shell以及许多其他因素而有所不同。
在这个特定的例子中,你列出了环境变量,因为你的main函数不仅传递了argv中的参数,而且还传递了envp中的环境变量列表,并且只是出于巧合,这些值被放置在argv数组的后面。记住,你永远不能相信这是真的。

main(int argc, char *argv[], char *envp[]);

总之,不要超过数组的末尾。它会导致坏的事情。
如果你的程序需要使用环境变量的值,你必须通过envp数组这样做,而不是通过argv数组滥用未定义的行为。

相关问题