我需要将十六进制为00 2C 00 21
的字符串作为我无法执行的命令行参数传递给我的程序。
#include<stdio.h>
int main(int argc,char* argv[]){
// argv[1] should have the string that the above hex represents
//... the program will use that string inside the program
//...also please explain what should i do if i (am/am not) allowed to modify the source
}
由于00是NULL字符,所以我无法在命令行中表示它并将其传递给程序。另外,我还需要传递由其他各种字符组成的字符串,这些字符的十六进制值如01或02(例如),您不能直接从键盘输入并作为参数传递。
我应该怎么做才能使我的程序接收到十六进制表示为00 2C 00 21
的字符串。
$./a.out " what should i write here? "
答案 0 :(得分:4)
您应该使程序接受包含转义符的字符串,然后自己解析它们。因此它将像这样被调用:
$ ./myprogram '\x00\x2c\x00\x21'
例如(\x
与C本身使用的匹配,因此用户可以熟悉)。单引号是为了保护反斜杠免受shell攻击,不是百分百确定的,并且目前没有适当的提示。
结果将不是字符串,因为C语言中的字符串不能包含0个字符。
这是一个看起来像的例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static size_t decode(void *buf, size_t buf_max, const char *s)
{
unsigned char *put = buf;
unsigned char * const put_max = put + buf_max;
while (*s != '\0' && put != put_max)
{
if (*s == '\\')
{
++s;
if (*s == '\\')
*put++ = *s++;
else if (*s == 'x')
{
++s;
char *endp;
const unsigned long v = strtoul(s, &endp, 16);
if (endp == s)
break;
*put++ = (unsigned char) v;
s = endp;
}
else
break;
}
else
*put++ = *s++;
}
return put - (unsigned char *) buf;
}
int main(int argc, char *argv[])
{
unsigned char buf[32];
const size_t len = decode(buf, sizeof buf, "\\x0hello\\x1\\xaa\\xfe\\xed");
for (size_t i = 0; i < len; ++i)
{
printf("%x\n", buf[i]);
}
return 0;
}
请注意,您要通过main()
中的测试“驱动程序”,例如argv[1]
至decode()
。双反斜杠可以防止C编译器的攻击,我们真的想以包含反斜杠转义符的字符串结尾。
答案 1 :(得分:2)
不可能使用bash或任何其他shell将零字节传递给程序参数。这仅仅是因为在C标准中是不可能的。
C标准说C11 5.1.2.2.1p2(强调我):
...主函数的参数应遵守以下约束:
-...
-如果argc的值大于零,则数组成员argv [0]至argv [argc-1](包含在内)应包含指向 strings 的指针,这些指针由主机环境赋予实现定义的值在程序启动之前。 ...
-...
“字符串”为C11 7.1.1p1(强调我的意思):
字符串是由终止的连续字符序列,其中包括第一个空字符。 ...字符串的长度是空字符之前的字节数,字符串的值是所包含字符的值的顺序。
“空字符”是byte with all bits set to 0
C11 5.2.1p2。它是零。在第一个“空字符”上,字符串终止。如果字符数组中嵌入了零字节,则该字符串不能为字符串(确切地说,请参见note 78,字符串文字可能不是字符串,因为它可以嵌入空字符) 。您不能将嵌入在C程序参数中的多个0x00
值传递给C程序,因为这不是您要传递的“字符串”。
它围绕它编写自己的解析器的正确方法,它将接受“字符串”(即./a.out "00 2C 00 21"
)并自己转换为零字节。
对于您的用例,如果很简单,那么我可以在另一个答案中介绍一个更简单的解析器。你可以传递一个参数,其中所有字节均递增1,然后在程序中递减1。
或者您可以传递特殊的字节值,例如ex。 0xff
(如果您的实现以及操作系统和环境支持传递0xff
字节)代替0x00
,然后将其替换为程序。此选项如下所示:
#include <string.h>
#include <stddef.h>
#include <assert.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
assert(argc >= 2);
for (size_t i = 0, max = strlen(argv[1]); i < max; ++i) {
// replace all 0xff by 0x00
if ( (0xff & argv[1][i]) == 0xff) {
argv[1][i] = 0x00;
}
}
// use argv[1]
for (size_t i = 0, max = 4; i < max; ++i) {
printf("argv[1][%d] = 0x%02x\n", i, 0xff & argv[1][i]);
}
}
并致电:
./a.out $'\xff\x2c\xff\x2c'
在repl.it上进行了测试。
bash将$'...'
解释为ANSI-C Quoting。 \xff
被解释为十六进制常量,因此第一个参数将等于(char[]){0xff, 0x2c, 0xff, 0x2c, 0x00}
。将0xff
替换为0x00
后,它将变为(char[]){0x00, 0x2c, 0x00, 0x2c, 0x00}
,您可以使用前4个字节。