将特殊字符作为参数传递

时间:2019-06-10 08:54:57

标签: c bash

我需要将十六进制为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?  " 

2 个答案:

答案 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个字节。

相关问题