二进制数据作为命令行参数
Binary data as command line argument
我有一个简单的 c++ 程序(还有一个类似的 c 程序(,它只打印出第一个参数
#include <iostream>
int main(int argc, char** argv)
{
if(argc > 1)
std::cout << ">>" << argv[1] << "<<n";
}
我可以传递二进制数据(我已经尝试过 bash(作为参数,例如
$./a.out $(printf "1x0123")
>>1?23<<
如果我尝试在那里传递一个空值,我会得到
./a.out $(printf "1x0023")
bash: warning: command substitution: ignored null byte in input
>>123<<
显然 bash(?
但是是否可以以这种方式发送 null 作为命令行参数? c或c ++对此有任何限制吗?
编辑:我没有在日常的c ++中使用它,这个问题只是出于好奇
这个答案是用 C 语言编写的,但可以编译为 C++ 并且在两者中的工作方式相同。 我引用 C11 标准;C++标准中有等效的定义。
没有将空字节传递给程序参数的好方法
C11 §5.1.2.2.1 程序启动:
如果argc
的值大于零,则argv[0]
到argv[argc-1]
(含(的数组成员应包含指向字符串的指针,这些字符串在程序启动之前由主机环境给出实现定义的值。C11 §7.1.1 术语
的定义 字符串是以第一个空字符结尾并包括第一个空字符的连续字符序列。
这意味着传递给main()
inargv
的每个参数都是以 null 结尾的字符串。 字符串末尾的空字节之后没有可靠的数据 — 搜索那里将访问字符串的边界之外。
因此,正如问题注释中详细指出的那样,在事件的正常过程中,不可能通过参数列表将空字节传递给程序,因为空字节被解释为每个参数的结尾。
通过特别协议
这并没有留下太多的回旋余地。 但是,如果调用/调用程序和被调用/调用程序都同意约定,那么,即使有标准施加的限制,您也可以将任意二进制数据(包括任意空字节序列(传递给调用的程序 — 直到实现施加的参数列表长度限制。
公约必须遵循以下方针:
- 所有参数(忽略的
argv[0]
和最后一个参数argv[argc-1]
除外(都由非空字节流后跟一个 null 组成。 - 如果需要相邻的 null,则必须在命令行上提供空参数。
- 如果需要尾随 null,则必须提供空参数作为命令行上的最后一个参数。
这可能会导致诸如 (null19.c
(:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void hex_dump(const char *tag, size_t size, const char *buffer);
int main(int argc, char **argv)
{
if (argc < 2)
{
fprintf(stderr, "Usage: %s arg1 [arg2 '' arg4 ...]n", argv[0]);
exit(EXIT_FAILURE);
}
size_t len_args = 0;
for (int i = 1; i < argc; i++)
len_args += strlen(argv[i]) + 1;
char buffer[len_args];
size_t offset = 0;
for (int i = 1; i < argc; i++)
{
size_t arglen = strlen(argv[i]) + 1;
memmove(buffer + offset, argv[i], strlen(argv[i]) + 1);
offset += arglen;
}
assert(offset != 0);
offset--;
hex_dump("Argument list", offset, buffer);
return 0;
}
static inline size_t min_size(size_t x, size_t y) { return (x < y) ? x : y; }
static void hex_dump(const char *tag, size_t size, const char *buffer)
{
printf("%s (%zu):n", tag, size);
size_t offset = 0;
while (size != 0)
{
printf("0x%.4zX:", offset);
size_t count = min_size(16, size);
for (size_t i = 0; i < count; i++)
printf(" %.2X", buffer[offset + i] & 0xFF);
putchar('n');
size -= count;
offset += count;
}
}
这可以使用以下方法调用:
$ ./null19 '1234' '5678' '' '' '' '' 'def0' ''
Argument list (19):
0x0000: 31 32 33 34 00 35 36 37 38 00 00 00 00 00 64 65
0x0010: 66 30 00
$
第一个参数被视为由 5 个字节组成 — 四个数字和一个空字节。 第二个是类似的。 第三个到第六个参数分别表示一个空字节(如果您需要大量连续的空字节,这会很痛苦(,然后还有另一个五个字节的字符串(三个字母,一个数字,一个空字节(。 最后一个参数为空,但确保末尾有一个空字节。 如果省略,输出将不包括最终的终端空字节。
$ ./null19 '1234' '5678' '' '' '' '' 'def0'
Argument list (18):
0x0000: 31 32 33 34 00 35 36 37 38 00 00 00 00 00 64 65
0x0010: 66 30
$
这与以前相同,只是数据中没有尾随的空字节。 问题中的两个示例很容易处理:
$ ./null19 $(printf "1x0123")
Argument list (4):
0x0000: 31 01 32 33
$ ./null19 1 23
Argument list (4):
0x0000: 31 00 32 33
$
这严格在标准中工作,假设只有空字符串被识别为有效参数。 实际上,这些参数在内存中已经是连续的,因此在许多平台上可以避免将阶段复制到缓冲区中。 但是,该标准并未规定参数字符串在内存中连续布局。
如果需要包含二进制数据的多个参数,可以修改约定。 例如,您可以采用字符串的控制参数,该参数指示组成一个逻辑二进制参数的后续物理参数的数量。
所有这些都依赖于程序按照约定解释参数列表。 这不是一个真正的通用解决方案。
- 如何在OMNET++中指定与命令行参数组合的输出文件名
- 如何处理linux终端中带有负号(-)的C++中的命令行参数
- 使用 C++ 将命令行参数拆分为参数/向量
- 如何在OMNET++中添加专门的命令行参数?
- 如何在不传递命令行参数的情况下在 c++ 中设置环境变量
- atoi() 在应用于大型命令行参数时会产生不正确的值
- 命令行参数,cant 或两个变量
- 在 Windows 中使用 boost::p rogram_options 从命令行参数读取 Unicode 字符
- 如何在 Android/NDK 上将命令行参数从 gradlew.bat 传递到 Clang
- 编写一个将 LLVM IR 文件作为命令行参数的程序
- 通过命令行参数获取llvm ir文件时面临问题
- 有没有办法根据命令行参数定义数组大小?运行时与编译时实例化?
- 如何在不使用文件扩展名的情况下使用命令行参数打开C++中的文本文件?
- 命令行参数在不到 3 个 LOC 中 int?
- 在命令行参数中使用引号
- 在VS2013中使用devenv (C++)传递命令行参数argv
- 二进制数据作为命令行参数
- 如何在 C++11 中创建具有命令行参数大小的动态数组?
- 需要有关如何设置getopt_long以正确传递命令行参数的建议
- Bash 脚本和命令行参数